Você está na página 1de 1145

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, fr
om such code.
The changes to the original code from Netscape Communications Corporation are do
cumented
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
#include
#include
#include
#include
#include
#include
#include
#include
#include

"jscntxt.h"
"jsdbgapi.h"
"jsemit.h"
"jsfun.h"
"jsgc.h"
"jslock.h"
"jsobj.h"
"jsparse.h"
"jsscope.h"
"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>
#endif

/* for isatty() */

#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
static
#endif
#endif

JSDEBUGGER_JAVA_UI
JSDJContext *_jsdjc;
/* JSDEBUGGER_JAVA_UI */
/* 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 en
tering 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 stackchunks
ize] [-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, ...)
#endif
"build()
"clear([obj])
0
};

Test JS_ConvertArguments",
Show build date and time",
Clear properties of object",

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_message
s[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,
{"height",
ITS_HEIGHT,
{"width",
ITS_WIDTH,
{"funny",
ITS_FUNNY,
{"array",
ITS_ARRAY,
{"rdonly",
ITS_RDONLY,
{0}
};

JSPROP_ENUMERATE},
JSPROP_ENUMERATE},
JSPROP_ENUMERATE},
JSPROP_ENUMERATE},
JSPROP_ENUMERATE},
JSPROP_READONLY},

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,
>")
MSG_DEF(JSMSG_NOT_DEFINED,
fined")
MSG_DEF(JSMSG_NO_REG_EXPS,

0, 0, JSEXN_NONE, "<Error #0 is reserved


1, 1, JSEXN_REFERENCEERR, "{0} is not de
2, 1, JSEXN_INTERNALERR, "sorry, regular

expression are not supported")


MSG_DEF(JSMSG_MORE_ARGS_NEEDED,
n {1} argument{2}")
MSG_DEF(JSMSG_BAD_CHAR,
ter {0}")
MSG_DEF(JSMSG_BAD_TYPE,
MSG_DEF(JSMSG_CANT_LOCK,
MSG_DEF(JSMSG_CANT_UNLOCK,
MSG_DEF(JSMSG_INCOMPATIBLE_PROTO,
called on incompatible {2}")
MSG_DEF(JSMSG_NO_CONSTRUCTOR,
r")
MSG_DEF(JSMSG_CANT_ALIAS,
} in class {2}")
MSG_DEF(JSMSG_NO_PROTO,
rototype.{0} is not yet implemented")
MSG_DEF(JSMSG_BAD_SORT_ARG,
otype.sort argument")
MSG_DEF(JSMSG_BAD_ATOMIC_NUMBER,
: no index for atom {0}")
MSG_DEF(JSMSG_TOO_MANY_LITERALS,
als")
MSG_DEF(JSMSG_CANT_WATCH,
e objects of class {0}")
MSG_DEF(JSMSG_STACK_UNDERFLOW,
compiling {0}: stack underflow at pc
MSG_DEF(JSMSG_NEED_DIET,
)
MSG_DEF(JSMSG_BAD_CASE,
ression")
MSG_DEF(JSMSG_READ_ONLY,
MSG_DEF(JSMSG_BAD_FORMAL,
parameter")
MSG_DEF(JSMSG_SAME_FORMAL,
ment {0}")
MSG_DEF(JSMSG_NOT_FUNCTION,
ion")
MSG_DEF(JSMSG_NOT_CONSTRUCTOR,
ructor")
MSG_DEF(JSMSG_STACK_OVERFLOW,
in {0}")
MSG_DEF(JSMSG_NOT_EXPORTED,
MSG_DEF(JSMSG_OVER_RECURSED,
sion")
MSG_DEF(JSMSG_IN_NOT_OBJECT,
nd {0}")
MSG_DEF(JSMSG_BAD_NEW_RESULT,
n result {0}")
MSG_DEF(JSMSG_BAD_SHARP_DEF,
definition #{0}=")
MSG_DEF(JSMSG_BAD_SHARP_USE,
use #{0}#")
MSG_DEF(JSMSG_BAD_INSTANCEOF_RHS,
f' operand {0}")
MSG_DEF(JSMSG_BAD_BYTECODE,
JavaScript bytecode {0}")
MSG_DEF(JSMSG_BAD_RADIX,
MSG_DEF(JSMSG_NAN,
MSG_DEF(JSMSG_CANT_CONVERT,
an integer")

3, 3, JSEXN_NONE, "{0} requires more tha


4, 1, JSEXN_NONE, "invalid format charac
5,
6,
7,
8,

1,
0,
0,
3,

JSEXN_NONE, "unknown type {0}")


JSEXN_NONE, "can't lock memory")
JSEXN_NONE, "can't unlock memory")
JSEXN_TYPEERR, "{0}.prototype.{1}

9, 1, JSEXN_NONE, "{0} has no constructo


10, 3, JSEXN_NONE, "can't alias {0} to {1
11, 1, JSEXN_INTERNALERR, "sorry, Array.p
12, 0, JSEXN_TYPEERR, "invalid Array.prot
13, 1, JSEXN_INTERNALERR, "internal error
14, 0, JSEXN_INTERNALERR, "too many liter
15, 1, JSEXN_NONE, "can't watch non-nativ
16, 2, JSEXN_INTERNALERR, "internal error
{1}")
17, 1, JSEXN_INTERNALERR, "{0} too large"
18, 0, JSEXN_SYNTAXERR, "invalid case exp
19, 1, JSEXN_ERR, "{0} is read-only")
20, 0, JSEXN_SYNTAXERR, "malformed formal
21, 1, JSEXN_NONE, "duplicate formal argu
22, 1, JSEXN_TYPEERR, "{0} is not a funct
23, 1, JSEXN_TYPEERR, "{0} is not a const
24, 1, JSEXN_INTERNALERR, "stack overflow
25, 1, JSEXN_NONE, "{0} is not exported")
26, 0, JSEXN_INTERNALERR, "too much recur
27, 1, JSEXN_TYPEERR, "invalid 'in' opera
28, 1, JSEXN_NONE, "invalid new expressio
29, 1, JSEXN_ERR, "invalid sharp variable
30, 1, JSEXN_ERR, "invalid sharp variable
31, 1, JSEXN_TYPEERR, "invalid 'instanceo
32, 1, JSEXN_INTERNALERR, "unimplemented
33, 1, JSEXN_ERR, "illegal radix {0}")
34, 1, JSEXN_ERR, "{0} is not a number")
35, 1, JSEXN_NONE, "can't convert {0} to

MSG_DEF(JSMSG_CYCLIC_VALUE,
MSG_DEF(JSMSG_PERMANENT,
MSG_DEF(JSMSG_CANT_CONVERT_TO,
to {1}")
MSG_DEF(JSMSG_NO_PROPERTIES,
ies")
MSG_DEF(JSMSG_CANT_FIND_CLASS,
0}")
MSG_DEF(JSMSG_CANT_XDR_CLASS,
MSG_DEF(JSMSG_BYTECODE_TOO_BIG,
oo large (limit {1})")
MSG_DEF(JSMSG_UNKNOWN_FORMAT,
de format {0}")
MSG_DEF(JSMSG_TOO_MANY_CON_ARGS,
ctor arguments")
MSG_DEF(JSMSG_TOO_MANY_FUN_ARGS,
n arguments")
MSG_DEF(JSMSG_BAD_QUANTIFIER,
er {0}")
MSG_DEF(JSMSG_MIN_TOO_BIG,
m {0}")
MSG_DEF(JSMSG_MAX_TOO_BIG,
m {0}")
MSG_DEF(JSMSG_OUT_OF_ORDER,
than minimum")
MSG_DEF(JSMSG_ZERO_QUANTIFIER,
{0}")
MSG_DEF(JSMSG_UNTERM_QUANTIFIER,
ntifier {0}")
MSG_DEF(JSMSG_EMPTY_BEFORE_STAR,
on before * could be empty")
MSG_DEF(JSMSG_EMPTY_BEFORE_PLUS,
on before + could be empty")
MSG_DEF(JSMSG_MISSING_PAREN,
enthetical {0}")
MSG_DEF(JSMSG_UNTERM_CLASS,
racter class {0}")
MSG_DEF(JSMSG_TRAILING_SLASH,
egular expression")
MSG_DEF(JSMSG_BAD_CLASS_RANGE,
character class")
MSG_DEF(JSMSG_BAD_FLAG,
expression flag {0}")
MSG_DEF(JSMSG_NO_INPUT,
}/{1}{2}")
MSG_DEF(JSMSG_CANT_OPEN,
MSG_DEF(JSMSG_BAD_STRING_MASK,
mask {0}")
MSG_DEF(JSMSG_NO_STRING_PROTO,
prototype.{0} is not yet implemented")
MSG_DEF(JSMSG_END_OF_DATA,
a")
MSG_DEF(JSMSG_SEEK_BEYOND_START,
tart")
MSG_DEF(JSMSG_SEEK_BEYOND_END,
nd")
MSG_DEF(JSMSG_END_SEEK,
k")
MSG_DEF(JSMSG_WHITHER_WHENCE,
{0}")

36, 1, JSEXN_ERR, "cyclic {0} value")


37, 1, JSEXN_ERR, "{0} is permanent")
38, 2, JSEXN_TYPEERR, "can't convert {0}
39, 1, JSEXN_TYPEERR, "{0} has no propert
40, 1, JSEXN_NONE, "can't find class id {
41, 1, JSEXN_NONE, "can't XDR class {0}")
42, 2, JSEXN_INTERNALERR, "bytecode {0} t
43, 1, JSEXN_INTERNALERR, "unknown byteco
44, 0, JSEXN_SYNTAXERR, "too many constru
45, 0, JSEXN_SYNTAXERR, "too many functio
46, 1, JSEXN_SYNTAXERR, "invalid quantifi
47, 1, JSEXN_SYNTAXERR, "overlarge minimu
48, 1, JSEXN_SYNTAXERR, "overlarge maximu
49, 1, JSEXN_SYNTAXERR, "maximum {0} less
50, 1, JSEXN_SYNTAXERR, "zero quantifier
51, 1, JSEXN_SYNTAXERR, "unterminated qua
52, 0, JSEXN_SYNTAXERR, "regular expressi
53, 0, JSEXN_SYNTAXERR, "regular expressi
54, 1, JSEXN_SYNTAXERR, "unterminated par
55, 1, JSEXN_SYNTAXERR, "unterminated cha
56, 0, JSEXN_SYNTAXERR, "trailing \\ in r
57, 0, JSEXN_SYNTAXERR, "invalid range in
58, 1, JSEXN_SYNTAXERR, "invalid regular
59, 3, JSEXN_SYNTAXERR, "no input for /{0
60, 2, JSEXN_NONE, "can't open {0}: {1}")
61, 1, JSEXN_ERR, "invalid string escape
62, 1, JSEXN_INTERNALERR, "sorry, String.
63, 0, JSEXN_NONE, "unexpected end of dat
64, 0, JSEXN_NONE, "illegal seek beyond s
65, 0, JSEXN_NONE, "illegal seek beyond e
66, 0, JSEXN_NONE, "illegal end-based see
67, 1, JSEXN_NONE, "unknown seek whence:

MSG_DEF(JSMSG_BAD_JVAL_TYPE,
} for XDR")
MSG_DEF(JSMSG_PAREN_BEFORE_FORMAL,
formal parameters")
MSG_DEF(JSMSG_MISSING_FORMAL,
arameter")
MSG_DEF(JSMSG_PAREN_AFTER_FORMAL,
formal parameters")
MSG_DEF(JSMSG_CURLY_BEFORE_BODY,
function body")
MSG_DEF(JSMSG_CURLY_AFTER_BODY,
function body")
MSG_DEF(JSMSG_PAREN_BEFORE_COND,
condition")
MSG_DEF(JSMSG_PAREN_AFTER_COND,
condition")
MSG_DEF(JSMSG_NO_IMPORT_NAME,
import statement")
MSG_DEF(JSMSG_NAME_AFTER_DOT,
er . operator")
MSG_DEF(JSMSG_BRACKET_IN_INDEX,
ex expression")
MSG_DEF(JSMSG_NO_EXPORT_NAME,
export statement")
MSG_DEF(JSMSG_PAREN_BEFORE_SWITCH,
switch expression")
MSG_DEF(JSMSG_PAREN_AFTER_SWITCH,
switch expression")
MSG_DEF(JSMSG_CURLY_BEFORE_SWITCH,
switch body")
MSG_DEF(JSMSG_COLON_AFTER_CASE,
case label")
MSG_DEF(JSMSG_WHILE_AFTER_DO,
ter do-loop body")
MSG_DEF(JSMSG_PAREN_AFTER_FOR,
for")
MSG_DEF(JSMSG_SEMI_AFTER_FOR_INIT,
for-loop initializer")
MSG_DEF(JSMSG_SEMI_AFTER_FOR_COND,
for-loop condition")
MSG_DEF(JSMSG_PAREN_AFTER_FOR_CTRL,
for-loop control")
MSG_DEF(JSMSG_CURLY_BEFORE_TRY,
try block")
MSG_DEF(JSMSG_CURLY_AFTER_TRY,
try block")
MSG_DEF(JSMSG_PAREN_BEFORE_CATCH,
catch")
MSG_DEF(JSMSG_CATCH_IDENTIFIER,
er in catch")
MSG_DEF(JSMSG_PAREN_AFTER_CATCH,
catch")
MSG_DEF(JSMSG_CURLY_BEFORE_CATCH,
catch block")
MSG_DEF(JSMSG_CURLY_AFTER_CATCH,
catch block")
MSG_DEF(JSMSG_CURLY_BEFORE_FINALLY,
finally block")
MSG_DEF(JSMSG_CURLY_AFTER_FINALLY,
finally block")

68, 1, JSEXN_NONE, "unknown jsval type {0


69, 0, JSEXN_SYNTAXERR, "missing ( before
70, 0, JSEXN_SYNTAXERR, "missing formal p
71, 0, JSEXN_SYNTAXERR, "missing ) after
72, 0, JSEXN_SYNTAXERR, "missing { before
73, 0, JSEXN_SYNTAXERR, "missing } after
74, 0, JSEXN_SYNTAXERR, "missing ( before
75, 0, JSEXN_SYNTAXERR, "missing ) after
76, 0, JSEXN_SYNTAXERR, "missing name in
77, 0, JSEXN_SYNTAXERR, "missing name aft
78, 0, JSEXN_SYNTAXERR, "missing ] in ind
79, 0, JSEXN_SYNTAXERR, "missing name in
80, 0, JSEXN_SYNTAXERR, "missing ( before
81, 0, JSEXN_SYNTAXERR, "missing ) after
82, 0, JSEXN_SYNTAXERR, "missing { before
83, 0, JSEXN_SYNTAXERR, "missing : after
84, 0, JSEXN_SYNTAXERR, "missing while af
85, 0, JSEXN_SYNTAXERR, "missing ( after
86, 0, JSEXN_SYNTAXERR, "missing ; after
87, 0, JSEXN_SYNTAXERR, "missing ; after
88, 0, JSEXN_SYNTAXERR, "missing ) after
89, 0, JSEXN_SYNTAXERR, "missing { before
90, 0, JSEXN_SYNTAXERR, "missing } after
91, 0, JSEXN_SYNTAXERR, "missing ( before
92, 0, JSEXN_SYNTAXERR, "missing identifi
93, 0, JSEXN_SYNTAXERR, "missing ) after
94, 0, JSEXN_SYNTAXERR, "missing { before
95, 0, JSEXN_SYNTAXERR, "missing } after
96, 0, JSEXN_SYNTAXERR, "missing { before
97, 0, JSEXN_SYNTAXERR, "missing } after

MSG_DEF(JSMSG_CATCH_OR_FINALLY,
finally after try")
MSG_DEF(JSMSG_PAREN_BEFORE_WITH,
with-statement object")
MSG_DEF(JSMSG_PAREN_AFTER_WITH,
with-statement object")
MSG_DEF(JSMSG_CURLY_IN_COMPOUND,
pound statement")
MSG_DEF(JSMSG_NO_VARIABLE_NAME,
name")
MSG_DEF(JSMSG_COLON_IN_COND,
ditional expression")
MSG_DEF(JSMSG_PAREN_AFTER_ARGS,
argument list")
MSG_DEF(JSMSG_BRACKET_AFTER_LIST,
element list")
MSG_DEF(JSMSG_COLON_AFTER_ID,
property id")
MSG_DEF(JSMSG_CURLY_AFTER_LIST,
property list")
MSG_DEF(JSMSG_PAREN_IN_PAREN,
enthetical")
MSG_DEF(JSMSG_SEMI_BEFORE_STMNT,
statement")
MSG_DEF(JSMSG_NO_RETURN_VALUE,
not always return a value")
MSG_DEF(JSMSG_DUPLICATE_FORMAL,
rgument {0}")
MSG_DEF(JSMSG_EQUAL_AS_ASSIGN,
) mistyped as assignment (=)?{0}")
MSG_DEF(JSMSG_BAD_IMPORT,
xpression")
MSG_DEF(JSMSG_TOO_MANY_DEFAULTS,
itch default")
MSG_DEF(JSMSG_TOO_MANY_CASES,
h cases")
MSG_DEF(JSMSG_BAD_SWITCH,
tatement")
MSG_DEF(JSMSG_BAD_FOR_LEFTSIDE,
eft-hand side")
MSG_DEF(JSMSG_CATCH_AFTER_GENERAL,
nditional catch")
MSG_DEF(JSMSG_CATCH_WITHOUT_TRY,
y")
MSG_DEF(JSMSG_FINALLY_WITHOUT_TRY,
try")
MSG_DEF(JSMSG_LABEL_NOT_FOUND,
)
MSG_DEF(JSMSG_TOUGH_BREAK,
MSG_DEF(JSMSG_BAD_CONTINUE,
")
MSG_DEF(JSMSG_BAD_RETURN,
MSG_DEF(JSMSG_BAD_LABEL,
MSG_DEF(JSMSG_DUPLICATE_LABEL,
)
MSG_DEF(JSMSG_VAR_HIDES_ARG,
argument")
MSG_DEF(JSMSG_BAD_VAR_INIT,
initialization")
MSG_DEF(JSMSG_BAD_LEFTSIDE_OF_ASS,

98, 0, JSEXN_SYNTAXERR, "missing catch or


99, 0, JSEXN_SYNTAXERR, "missing ( before
100, 0, JSEXN_SYNTAXERR, "missing ) after
101, 0, JSEXN_SYNTAXERR, "missing } in com
102, 0, JSEXN_SYNTAXERR, "missing variable
103, 0, JSEXN_SYNTAXERR, "missing : in con
104, 0, JSEXN_SYNTAXERR, "missing ) after
105, 0, JSEXN_SYNTAXERR, "missing ] after
106, 0, JSEXN_SYNTAXERR, "missing : after
107, 0, JSEXN_SYNTAXERR, "missing } after
108, 0, JSEXN_SYNTAXERR, "missing ) in par
109, 0, JSEXN_SYNTAXERR, "missing ; before
110, 1, JSEXN_TYPEERR, "function {0} does
111, 1, JSEXN_TYPEERR, "duplicate formal a
112, 1, JSEXN_NONE, "test for equality (==
113, 0, JSEXN_SYNTAXERR, "invalid import e
114, 0, JSEXN_SYNTAXERR, "more than one sw
115, 0, JSEXN_INTERNALERR, "too many switc
116, 0, JSEXN_SYNTAXERR, "invalid switch s
117, 0, JSEXN_SYNTAXERR, "invalid for/in l
118, 0, JSEXN_SYNTAXERR, "catch after unco
119, 0, JSEXN_SYNTAXERR, "catch without tr
120, 0, JSEXN_SYNTAXERR, "finally without
121, 0, JSEXN_SYNTAXERR, "label not found"
122, 0, JSEXN_SYNTAXERR, "invalid break")
123, 0, JSEXN_SYNTAXERR, "invalid continue
124, 0, JSEXN_SYNTAXERR, "invalid return")
125, 0, JSEXN_SYNTAXERR, "invalid label")
126, 0, JSEXN_SYNTAXERR, "duplicate label"
127, 1, JSEXN_TYPEERR, "variable {0} hides
128, 0, JSEXN_SYNTAXERR, "invalid variable
129, 0, JSEXN_SYNTAXERR, "invalid assignme

nt left-hand side")
MSG_DEF(JSMSG_BAD_OPERAND,
130, 1, JSEXN_SYNTAXERR, "invalid {0} oper
and")
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 reserve
d 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 va
riable definition")
MSG_DEF(JSMSG_BAD_PROTOTYPE,
135, 1, JSEXN_TYPEERR, "'prototype' prop
erty 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 str
ing literal")
MSG_DEF(JSMSG_NESTED_COMMENT,
139, 0, JSEXN_SYNTAXERR, "nested comment")
MSG_DEF(JSMSG_UNTERMINATED_COMMENT, 140, 0, JSEXN_SYNTAXERR, "unterminated com
ment")
MSG_DEF(JSMSG_UNTERMINATED_REGEXP,
141, 0, JSEXN_SYNTAXERR, "unterminated reg
ular expression literal")
MSG_DEF(JSMSG_BAD_REGEXP_FLAG,
142, 0, JSEXN_SYNTAXERR, "invalid flag aft
er 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 characte
r")
MSG_DEF(JSMSG_BAD_OCTAL,
145, 1, JSEXN_NONE, "{0} is not a legal EC
MA-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 e
xceeds 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} usag
e")
MSG_DEF(JSMSG_BAD_ARRAY_LENGTH,
152, 0, JSEXN_RANGEERR, "invalid array len
gth")
MSG_DEF(JSMSG_CANT_DESCRIBE_PROPS,
153, 1, JSEXN_NONE, "can't describe non-na
tive 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 unde
clared 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 seque
nce")
MSG_DEF(JSMSG_GETTER_ONLY,
160, 0, JSEXN_TYPEERR, "setting a property
that has only a getter")

MSG_DEF(JSMSG_TRAILING_COMMA,
161, 0,
s not legal in ECMA-262 object initializers")
MSG_DEF(JSMSG_UNDEFINED_PROP,
162, 1,
ined property {0}")
MSG_DEF(JSMSG_USELESS_EXPR,
163, 0,
")
MSG_DEF(JSMSG_REDECLARED_PARAM,
164, 1,
ormal parameter {0}")
MSG_DEF(JSMSG_NEWREGEXP_FLAGGED,
165, 0,
when constructing one RegExp from another")

JSEXN_SYNTAXERR, "trailing comma i


JSEXN_TYPEERR, "reference to undef
JSEXN_TYPEERR, "useless expression
JSEXN_TYPEERR, "redeclaration of f
JSEXN_TYPEERR, "can't supply flags

**** 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
#define
#define
#define
#define
#define
#define

ATOM_OFFSET(name)
OFFSET_TO_ATOM(rt,off)
TAG_ATOM_OFFSET(name)
TAG_CHAR_STRING(name)
UNTAG_ATOM_OFFSET(ptr)
UNTAG_CHAR_STRING(ptr)
IS_ATOM_OFFSET(ptr)

offsetof(JSAtomState, name##Atom)
(*(JSAtom **)((char*)&(rt)->atomState + (off)))
((const char *) ATOM_OFFSET(name))
name
((size_t)(ptr))
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
{js_InitStringClass,
{js_InitStringClass,
#endif
{js_InitStringClass,
{js_InitStringClass,
{js_InitStringClass,
{js_InitStringClass,
#if JS_HAS_UNEVAL
{js_InitStringClass,
#endif
/* Exception constructors.
#if JS_HAS_ERROR_EXCEPTIONS
{js_InitExceptionClasses,
{js_InitExceptionClasses,
{js_InitExceptionClasses,
{js_InitExceptionClasses,
{js_InitExceptionClasses,
{js_InitExceptionClasses,
{js_InitExceptionClasses,
{js_InitExceptionClasses,
#endif
{NULL,

*/
in a backward-compatible way by the DOM. */
TAG_CHAR_STRING(js_escape_str)},
TAG_CHAR_STRING(js_unescape_str)},
TAG_CHAR_STRING(js_decodeURI_str)},
TAG_CHAR_STRING(js_encodeURI_str)},
TAG_CHAR_STRING(js_decodeURIComponent_str)},
TAG_CHAR_STRING(js_encodeURIComponent_str)},
TAG_CHAR_STRING(js_uneval_str)},
*/
TAG_ATOM_OFFSET(Error)},
TAG_CHAR_STRING(js_InternalError_str)},
TAG_CHAR_STRING(js_EvalError_str)},
TAG_CHAR_STRING(js_RangeError_str)},
TAG_CHAR_STRING(js_ReferenceError_str)},
TAG_CHAR_STRING(js_SyntaxError_str)},
TAG_CHAR_STRING(js_TypeError_str)},
TAG_CHAR_STRING(js_URIError_str)},
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
#undef
#undef
#undef
#undef
#undef
#undef

ATOM_OFFSET
OFFSET_TO_ATOM
TAG_ATOM_OFFSET
TAG_CHAR_STRING
UNTAG_ATOM_OFFSET
UNTAG_CHAR_STRING
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;
}
/*
*
n)
*
*

DREAMWEAVER
jschang, Adobe - (new function added 11/12/99 for OptionsArray setter functio
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 f
or 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
*/
#define
#define
#define
#define
#define
/* Type
#define
#define
#define
#define
#define
#define

tags stored in the low bits of a jsval.


JSVAL_OBJECT
JSVAL_INT
JSVAL_DOUBLE
JSVAL_STRING
JSVAL_BOOLEAN

0x0
0x1
0x2
0x4
0x6

/*
/*
/*
/*
/*

untagged reference to object */


tagged 31-bit integer value */
tagged reference to double */
tagged reference to string */
tagged boolean value */

tag bitfield length and derived macros. */


JSVAL_TAGBITS
3
JSVAL_TAGMASK
JS_BITMASK(JSVAL_TAGBITS)
JSVAL_TAG(v)
((v) & JSVAL_TAGMASK)
JSVAL_SETTAG(v,t)
((v) | (t))
JSVAL_CLRTAG(v)
((v) & ~(jsval)JSVAL_TAGMASK)
JSVAL_ALIGN
JS_BIT(JSVAL_TAGBITS)

/* Predicates for type testing.


#define JSVAL_IS_OBJECT(v)
#define JSVAL_IS_NUMBER(v)
#define JSVAL_IS_INT(v)
#define JSVAL_IS_DOUBLE(v)
#define JSVAL_IS_STRING(v)
#define JSVAL_IS_BOOLEAN(v)
#define JSVAL_IS_NULL(v)
#define JSVAL_IS_VOID(v)
#define JSVAL_IS_PRIMITIVE(v)

*/
(JSVAL_TAG(v) == JSVAL_OBJECT)
(JSVAL_IS_INT(v) || JSVAL_IS_DOUBLE(v))
(((v) & JSVAL_INT) && (v) != JSVAL_VOID)
(JSVAL_TAG(v) == JSVAL_DOUBLE)
(JSVAL_TAG(v) == JSVAL_STRING)
(JSVAL_TAG(v) == JSVAL_BOOLEAN)
((v) == JSVAL_NULL)
((v) == JSVAL_VOID)
(!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))

\
\

#define JSVAL_UNLOCK(cx,v)

/* Domain limits for the jsval


#define JSVAL_INT_POW2(n)
#define JSVAL_INT_MIN
#define JSVAL_INT_MAX
#define INT_FITS_IN_JSVAL(i)
#define JSVAL_TO_INT(v)
#define INT_TO_JSVAL(i)

: JS_TRUE)
(JSVAL_IS_GCTHING(v)
\
? JS_UnlockGCThing(cx, JSVAL_TO_GCTHING(v)) \
: JS_TRUE)
int type. */
((jsval)1 << (n))
((jsval)1 - JSVAL_INT_POW2(30))
(JSVAL_INT_POW2(30) - 1)
((jsuint)((i)+JSVAL_INT_MAX) <= 2*JSVAL_INT_MAX)
((jsint)(v) >> 1)
(((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
#define JSFUN_GETTER
JSPROP_GETTER
#define JSFUN_SETTER
JSPROP_SETTER
#define JSFUN_BOUND_METHOD
0x40
/* bind this to fun->object's
#define JSFUN_HEAVYWEIGHT
0x80
/* activation requires a Call
#define JSFUN_FLAGS_MASK
0xf0
/* overlay JSFUN_* attributes

etc. */
parent */
object */
*/

/*
* 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,
*/
#define JS_NewRuntime
#define JS_DestroyRuntime
#define JS_LockRuntime
#define JS_UnlockRuntime

contexts, and memory allocation.


JS_Init
JS_Finish
JS_Lock
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
JSFinalizeOp

convert;
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
#define JSCLASS_NEW_ENUMERATE
#define JSCLASS_NEW_RESOLVE
#define JSCLASS_PRIVATE_IS_NSISUPPORTS
#define JSCLASS_SHARE_ALL_PROPERTIES
#define JSCLASS_FW_SIMPLE_GET
GetProperty */
#define JSCLASS_FW_ENUM

0x01
0x02
0x04
0x08
0x10

/* objects have private slot */


/* has JSNewEnumerateOp hook */
/* has JSNewResolveOp hook */
/* private is (nsISupports *) */
/* all properties are SHARED */
0x20
/* class does simple js_
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
#define JSRESOLVE_ASSIGNING

0x01
0x02

/* resolve a qualified property id */


/* 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
uintN
const
const
const
const
uintN
uintN
const
const

char
char
char
jschar
jschar
jschar
jschar

*filename;
lineno;
*linebuf;
*tokenptr;
*uclinebuf;
*uctokenptr;
flags;
errorNumber;
*ucmessage;
**messageArgs;

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

source file name, URL, etc., or null */


source line number */
offending source line without final \n */
pointer to error token in linebuf */
unicode (original) line buffer */
unicode (original) token pointer */
error/warning, etc. */
the error number, e.g. see js.msg */
the (default) error message */
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
#define JSREG_GLOB
0x02
#define JSREG_MULTILINE 0x04

/* fold uppercase to lowercase */


/* global exec, creates array of matches */
/* 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 al
ign)
{
#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,
fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,

"\n%s allocation statistics:\n",


"
number of arenas:
"
number of allocations:
" number of free arena reclaims:
"
number of malloc calls:
"
number of deallocations:
" number of allocation growths:
"
number of in-place growths:
" number of realloc'ing growths:
"number of released allocations:
"
number of fast releases:
"
total bytes allocated:
"
mean allocation size:
"
standard deviation:
"
maximum allocation size:

stats->name);
%u\n", stats->narenas);
%u\n", stats->nallocs);
%u\n", stats->nreclaims);
%u\n", stats->nmallocs);
%u\n", stats->ndeallocs);
%u\n", stats->ngrows);
%u\n", stats->ninplace);
%u\n", stats->nreallocs);
%u\n", stats->nreleases);
%u\n", stats->nfastrels);
%u\n", stats->nbytes);
%g\n", mean);
%g\n", sigma);
%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
jsuword
jsuword
jsuword
};

{
*next;
base;
limit;
avail;

/*
/*
/*
/*

next arena for this lifetime */


aligned base address, follows this header */
one beyond last byte in arena */
points to next available byte */

#ifdef JS_ARENAMETER
typedef struct JSArenaStats JSArenaStats;
struct JSArenaStats {
JSArenaStats *next;

/* next in arenaStats list */

char
uint32
uint32
uint32
uint32
uint32
uint32
uint32
uint32
uint32
uint32
size_t
size_t
double

*name;
narenas;
nallocs;
nreclaims;
nmallocs;
ndeallocs;
ngrows;
ninplace;
nreallocs;
nreleases;
nfastrels;
nbytes;
maxalloc;
variance;

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

name for debugging */


number of arenas in pool */
number of JS_ARENA_ALLOCATE() calls */
number of reclaims from freeArenas */
number of malloc() calls */
number of lifetime deallocations */
number of JS_ARENA_GROW() calls */
number of in-place growths */
number of arena grow extending reallocs */
number of JS_ARENA_RELEASE() calls */
number of "fast path" releases */
total bytes allocated */
maximum allocation size in bytes */
size variance accumulator */

/*
/*
/*
/*

first arena in pool list */


arena from which to allocate space */
net exact size of a new arena */
alignment mask (power-of-2 - 1) */

};
#endif
struct JSArenaPool {
JSArena
first;
JSArena
*current;
size_t
arenasize;
jsuword
mask;
#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)
#define JS_UPTRDIFF(p,q)
#ifdef DEBUG
#define JS_FREE_PATTERN
#define JS_CLEAR_UNUSED(a)
#define JS_CLEAR_ARENA(a)
#else
#define JS_CLEAR_UNUSED(a)
#define JS_CLEAR_ARENA(a)
#endif

((void *) (pool)->current->avail)
((jsuword)(p) - (jsuword)(q))
0xDA
(JS_ASSERT((a)->avail <= (a)->limit),
memset((void*)(a)->avail, JS_FREE_PATTERN,
(a)->limit - (a)->avail))
memset((void*)(a), JS_FREE_PATTERN,
(a)->limit - (jsuword)(a))

\
\
\

/* nothing */
/* nothing */

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

JS_ArenaCountAllocation(ap, nb)
JS_ArenaCountInplaceGrowth(ap, size, incr)
JS_ArenaCountGrowth(ap, size, incr)
JS_ArenaCountRelease(ap, mark)
JS_ArenaCountRetract(ap, mark)

/*
/*
/*
/*
/*

nothing
nothing
nothing
nothing
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_EnumerateStub, JS_ResolveStub,
JSCLASS_NO_OPTIONAL_MEMBERS

JS_PropertyStub,
array_convert,

JS_PropertyStub,
JS_FinalizeStub,

};
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.toLocaleStrin
gAtom, 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
static
static
static

jschar
jschar
JSString
JSString

comma_space_ucstr[]
comma_ucstr[]
comma_space
comma

=
=
=
=

{',', ' ', 0};


{',', 0};
{2, comma_space_ucstr};
{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;
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
override
//
tions
//
ject
//
Array,
//
0] (the
//
tree)
//
pdated).
//
//
o that I
//
if

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
> the default implementation of the Array type. I implemented the op
> property on the select object so that it returns an OptionsArray ob
> instead of an Array object. The OptionsArray type is a subclass of
> but it has its own implementation for getting the value of options[
> value is sucked out of the corresponding TTAG_OPTION Run in the Run
> and setting the value of options[0] (the value is the Run tree is u
>
> I found that I needed to change the base implementation of Array, s
> could create a sub-class of it (OptionsArray).
(!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
JSContext
jsval
JSBool
} CompareArgs;

CompareArgs {
*context;
fval;
status;

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,
#endif
{js_toString_str,
array_toString,
{js_toLocaleString_str, array_toLocaleString,
/* Perl-ish methods. */
{"join",
array_join,
{"reverse",
array_reverse,
{"sort",
array_sort,
#ifdef NOTYET
{"pack",
array_pack,
#endif
{"push",
array_push,
{"pop",
array_pop,
{"shift",
array_shift,
{"unshift",
array_unshift,
{"splice",
array_splice,

0,0,0},
0,0,0},
0,0,0},
1,0,0},
0,0,0},
1,0,0},
1,0,0},
1,0,0},
0,0,0},
0,0,0},
1,0,0},
1,0,0},

/* Python-esque sequence methods. */


#if JS_HAS_SEQUENCE_OPS
{"concat",
array_concat,
{"slice",
array_slice,
#endif

0,0,0},
0,0,0},

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

char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char

#define
#define
#define
)
#define

js_Arguments_str[]
js_Array_str[]
js_Boolean_str[]
js_Call_str[]
js_Date_str[]
js_Function_str[]
js_Math_str[]
js_Number_str[]
js_Object_str[]
js_RegExp_str[]
js_Script_str[]
js_String_str[]
js_anonymous_str[]
js_arguments_str[]
js_arity_str[]
js_callee_str[]
js_caller_str[]
js_class_prototype_str[]
js_constructor_str[]
js_count_str[]
js_eval_str[]
js_getter_str[]
js_get_str[]
js_index_str[]
js_input_str[]
js_length_str[]
js_name_str[]
js_parent_str[]
js_proto_str[]
js_setter_str[]
js_set_str[]
js_toSource_str[]
js_toString_str[]
js_toLocaleString_str[]
js_valueOf_str[]

=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=

"Arguments";
"Array";
"Boolean";
"Call";
"Date";
"Function";
"Math";
"Number";
"Object";
"RegExp";
"Script";
"String";
"anonymous";
"arguments";
"arity";
"callee";
"caller";
"prototype";
"constructor";
"__count__";
"eval";
"getter";
"get";
"index";
"input";
"length";
"name";
"__parent__";
"__proto__";
"setter";
"set";
"toSource";
"toString";
"toLocaleString";
"valueOf";

HASH_OBJECT(o) ((JSHashNumber)(o) >> JSVAL_TAGBITS)


HASH_INT(i)
((JSHashNumber)(i))
HASH_DOUBLE(dp) ((JSHashNumber)(((uint32*)(dp))[0] ^ ((uint32*)(dp))[1])
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],
FROB(booleanAtoms[1],
FROB(nullAtom,

js_false_str);
js_true_str);
js_null_str);

FROB(ArgumentsAtom,
FROB(ArrayAtom,
FROB(BooleanAtom,

js_Arguments_str);
js_Array_str);
js_Boolean_str);

\
\
\
\

FROB(CallAtom,
FROB(DateAtom,
FROB(ErrorAtom,
FROB(FunctionAtom,
FROB(MathAtom,
FROB(NumberAtom,
FROB(ObjectAtom,
FROB(RegExpAtom,
FROB(ScriptAtom,
FROB(StringAtom,
FROB(anonymousAtom,
FROB(argumentsAtom,
FROB(arityAtom,
FROB(calleeAtom,
FROB(callerAtom,
FROB(classPrototypeAtom,
FROB(constructorAtom,
FROB(countAtom,
FROB(evalAtom,
FROB(getAtom,
FROB(getterAtom,
FROB(indexAtom,
FROB(inputAtom,
FROB(lengthAtom,
FROB(nameAtom,
FROB(parentAtom,
FROB(protoAtom,
FROB(setAtom,
FROB(setterAtom,
FROB(toSourceAtom,
FROB(toStringAtom,
FROB(toLocaleStringAtom,
FROB(valueOfAtom,

js_Call_str);
js_Date_str);
js_Error_str);
js_Function_str);
js_Math_str);
js_Number_str);
js_Object_str);
js_RegExp_str);
js_Script_str);
js_String_str);
js_anonymous_str);
js_arguments_str);
js_arity_str);
js_callee_str);
js_caller_str);
js_class_prototype_str);
js_constructor_str);
js_count_str);
js_eval_str);
js_get_str);
js_getter_str);
js_index_str);
js_input_str);
js_length_str);
js_name_str);
js_parent_str);
js_proto_str);
js_set_str);
js_setter_str);
js_toSource_str);
js_toString_str);
js_toLocaleString_str);
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)
#define ALIGN(b,t)

JS_MAX(JSVAL_ALIGN, sizeof(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
#define
#define
#define
#define

ATOM_PINNED
ATOM_INTERNED
ATOM_MARK
ATOM_NOCOPY
ATOM_TMPSTR

struct JSAtom {
JSHashEntry
uint8
int8
jsatomid
};
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define

0x01
0x02
0x04
0x40
0x80

/*
/*
/*
/*
/*

atom is pinned against GC */


pinned variant for JS_Intern* API */
atom is reachable via GC */
don't copy atom string bytes */
internal, to avoid extra string */

entry;
flags;
kwindex;
number;

/*
/*
/*
/*

key is jsval, value keyword info


pinned, interned, and mark flags
keyword index, -1 if not keyword
atom serial number and hash code

ATOM_KEY(atom)
ATOM_IS_OBJECT(atom)
ATOM_TO_OBJECT(atom)
ATOM_IS_INT(atom)
ATOM_TO_INT(atom)
ATOM_IS_DOUBLE(atom)
ATOM_TO_DOUBLE(atom)
ATOM_IS_STRING(atom)
ATOM_TO_STRING(atom)
ATOM_IS_BOOLEAN(atom)
ATOM_TO_BOOLEAN(atom)
ATOM_BYTES(atom)

*/
*/
*/
*/

((jsval)(atom)->entry.key)
JSVAL_IS_OBJECT(ATOM_KEY(atom))
JSVAL_TO_OBJECT(ATOM_KEY(atom))
JSVAL_IS_INT(ATOM_KEY(atom))
JSVAL_TO_INT(ATOM_KEY(atom))
JSVAL_IS_DOUBLE(ATOM_KEY(atom))
JSVAL_TO_DOUBLE(ATOM_KEY(atom))
JSVAL_IS_STRING(ATOM_KEY(atom))
JSVAL_TO_STRING(ATOM_KEY(atom))
JSVAL_IS_BOOLEAN(ATOM_KEY(atom))
JSVAL_TO_BOOLEAN(ATOM_KEY(atom))
JS_GetStringBytes(ATOM_TO_STRING(atom))

struct JSAtomListElement {
JSHashEntry
entry;
};
#define
#define
#define
#define

ALE_ATOM(ale)
ALE_INDEX(ale)
ALE_JSOP(ale)
ALE_NEXT(ale)

#define
#define
#define
#define

ALE_SET_ATOM(ale,atom) ((ale)->entry.key = (const void *)(atom))


ALE_SET_INDEX(ale,index)((ale)->entry.value = (void *)(index))
ALE_SET_JSOP(ale,op)
((ale)->entry.value = (void *)(op))
ALE_SET_NEXT(ale,link) ((ale)->entry.next = (JSHashEntry *)(link))

struct JSAtomList {
JSAtomListElement
JSHashTable

((JSAtom *) (ale)->entry.key)
((jsatomid) (ale)->entry.value)
((JSOp) (ale)->entry.value)
((JSAtomListElement *) (ale)->entry.next)

*list;
*table;

/* literals indexed for mapping */


/* 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
jsatomid
};

**vector;
length;

/* array of ptrs to indexed atoms */


/* count of (to-be-)indexed atoms */

struct JSAtomState {
JSRuntime
JSHashTable
jsatomid
jsatomid

*runtime;
*table;
number;
interns;

/*
/*
/*
/*

runtime that owns us */


hash table containing all atoms */
one beyond greatest atom number */
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
JSAtom
JSAtom
JSAtom
JSAtom
JSAtom
JSAtom
JSAtom
JSAtom
JSAtom
JSAtom
JSAtom
JSAtom
JSAtom
JSAtom
JSAtom
JSAtom
JSAtom
JSAtom
JSAtom
JSAtom
JSAtom
JSAtom
JSAtom
JSAtom
JSAtom

*RegExpAtom;
*ScriptAtom;
*StringAtom;
*anonymousAtom;
*argumentsAtom;
*arityAtom;
*calleeAtom;
*callerAtom;
*classPrototypeAtom;
*constructorAtom;
*countAtom;
*evalAtom;
*getAtom;
*getterAtom;
*indexAtom;
*inputAtom;
*lengthAtom;
*nameAtom;
*parentAtom;
*protoAtom;
*setAtom;
*setterAtom;
*toLocaleStringAtom;
*toSourceAtom;
*toStringAtom;
*valueOfAtom;

#ifdef JS_THREADSAFE
JSThinLock
volatile uint32
#endif
};

lock;
tablegen;

/* Well-known predefined strings and their atoms. */


extern const char *js_type_str[];
extern const char *js_boolean_str[];
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern

const
const
const
const
const
const
const
const
const
const
const
const
const
const
const
const
const
const
const
const
const
const
const

char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char
char

js_Arguments_str[];
js_Array_str[];
js_Boolean_str[];
js_Call_str[];
js_Date_str[];
js_Function_str[];
js_Math_str[];
js_Number_str[];
js_Object_str[];
js_RegExp_str[];
js_Script_str[];
js_String_str[];
js_anonymous_str[];
js_arguments_str[];
js_arity_str[];
js_callee_str[];
js_caller_str[];
js_class_prototype_str[];
js_constructor_str[];
js_count_str[];
js_eval_str[];
js_getter_str[];
js_get_str[];

extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern

const
const
const
const
const
const
const
const
const
const
const
const

char
char
char
char
char
char
char
char
char
char
char
char

js_index_str[];
js_input_str[];
js_length_str[];
js_name_str[];
js_parent_str[];
js_proto_str[];
js_setter_str[];
js_set_str[];
js_toSource_str[];
js_toString_str[];
js_toLocaleString_str[];
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_LONG1))))
#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_LON
G-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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

"jsstddef.h"
"jstypes.h"
"jsutil.h" /* Added by JSIFY */
"jsapi.h"
"jsatom.h"
"jsbool.h"
"jscntxt.h"
"jsconfig.h"
"jsinterp.h"
"jslock.h"
"jsnum.h"
"jsobj.h"
"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,
#endif
{js_toString_str, bool_toString,
{js_valueOf_str,
bool_valueOf,
{0,0,0,0,0}
};

0,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
*/
#define JS_NEXT_LINK(_e)
((_e)->next)
/*
** Return the element preceding
*/
#define JS_PREV_LINK(_e)
((_e)->prev)

element "_e"
\
element "_e"
\

/*
** 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
jsdouble
jsdouble

*jsNaN;
*jsNegativeInfinity;
*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)
# define JS_RUNTIME_UNMETER(rt, which)
#else
# define JS_RUNTIME_METER(rt, which)
# define JS_RUNTIME_UNMETER(rt, which)
#endif
#define JS_ENABLE_GC(rt)

JS_ATOMIC_INCREMENT(&(rt)->which)
JS_ATOMIC_DECREMENT(&(rt)->which)
/* nothing */
/* nothing */

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)
struct JSContext {
JSCList

((jsval *)(sh) + 2)

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

JS_BUG_AUTO_INDEX_PROPS
JS_BUG_NULL_INDEX_PROPS
JS_BUG_EMPTY_INDEX_ZERO
JS_BUG_SHORT_CIRCUIT
JS_BUG_EAGER_TOSTRING
JS_BUG_VOID_TOSTRING
JS_BUG_EVAL_THIS_FUN
JS_BUG_EVAL_THIS_SCOPE
JS_BUG_FALLIBLE_EQOPS
JS_BUG_FALLIBLE_TONUM
JS_BUG_WITH_CLOSURE
JS_BUG_SET_ENUMERATE

1
1
1
1
1
0
0
0
1
1
0
1

#define JS_HAS_PROP_DELETE
0
#define JS_HAS_CALL_OBJECT
0
#define JS_HAS_LABEL_STATEMENT 0

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

new object o: o.p = v sets o[0] */


o[0] defaults to null, not void */
o[""] is equivalent to o[0] */
1 && 1 => true, 1 && 0 => 0 bug */
o.toString() trumps o.valueOf() */
void 0 + 0 == "undefined0" */
eval('this') in function f is f */
Math.eval('sin(x)') vs. local x */
fallible/intransitive equality ops */
fallible ValueToNumber primitive */
with(o)function f(){} sets o.f */
o.p=q flags o.p JSPROP_ENUMERATE */

/* delete o.p removes p from o */


/* fun.caller is stack frame obj */
/* has break/continue to label: */

#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define

JS_HAS_DO_WHILE_LOOP
JS_HAS_SWITCH_STATEMENT
JS_HAS_SOME_PERL_FUN
JS_HAS_MORE_PERL_FUN
JS_HAS_VALUEOF_HINT
JS_HAS_LEXICAL_CLOSURE
JS_HAS_APPLY_FUNCTION
JS_HAS_CALL_FUNCTION
JS_HAS_OBJ_PROTO_PROP
JS_HAS_REGEXPS
JS_HAS_SEQUENCE_OPS
JS_HAS_INITIALIZERS
JS_HAS_OBJ_WATCHPOINT
JS_HAS_EXPORT_IMPORT
JS_HAS_EVAL_THIS_SCOPE
JS_HAS_TRIPLE_EQOPS
JS_HAS_SHARP_VARS
JS_HAS_REPLACE_LAMBDA
JS_HAS_SCRIPT_OBJECT
JS_HAS_XDR
JS_HAS_EXCEPTIONS
JS_HAS_UNDEFINED
JS_HAS_TOSOURCE
JS_HAS_IN_OPERATOR
JS_HAS_INSTANCEOF
JS_HAS_ARGS_OBJECT
JS_HAS_DEBUGGER_KEYWORD
JS_HAS_ERROR_EXCEPTIONS
JS_HAS_CATCH_GUARD
JS_HAS_NEW_OBJ_METHODS
JS_HAS_SPARSE_ARRAYS
JS_HAS_DFLT_MSG_STRINGS
JS_HAS_NUMBER_FORMATS
JS_HAS_GETTER_SETTER
JS_HAS_UNEVAL
JS_HAS_CONST
JS_HAS_FUN_EXPR_STMT
JS_HAS_LVALUE_RETURN

0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0

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

has do {...} while (b) */


has switch (v) {case c: ...} */
has array.join/reverse/sort */
has array.push, str.substr, etc */
valueOf(hint) where hint is typeof */
nested functions, lexically closed */
has fun.apply(obj, argArray) */
has fun.call(obj, arg1, ... argN) */
has o.__proto__ etc. */
has perl r.e.s via RegExp, /pat/ */
has array.slice, string.concat */
has var o = {'foo': 42, 'bar':3} */
has o.watch and o.unwatch */
has export fun; import obj.fun */
Math.eval is same as with (Math) */
has === and !== identity eqops */
has #n=, #n# for object literals */
has string.replace(re, lambda) */
has (new Script("x++")).exec() */
has XDR API and object methods */
has exception handling */
has global "undefined" property */
has Object/Array toSource method */
has in operator ('p' in {p:1}) */
has {p:1} instanceof Object */
has minimal ECMA arguments object */
has hook for debugger keyword */
has error object hierarchy */
has exception handling catch guard */
has Object.prototype query methods */
array methods preserve empty elems */
provides English error messages */
numbers have formatting methods */
has JS2 getter/setter functions */
has uneval() top-level function */
has JS2 const as alternative var */
has function expression statement */
has o.item(i) = j; for native item */

#elif JS_VERSION == 110


#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define

JS_BUG_AUTO_INDEX_PROPS
JS_BUG_NULL_INDEX_PROPS
JS_BUG_EMPTY_INDEX_ZERO
JS_BUG_SHORT_CIRCUIT
JS_BUG_EAGER_TOSTRING
JS_BUG_VOID_TOSTRING
JS_BUG_EVAL_THIS_FUN
JS_BUG_EVAL_THIS_SCOPE
JS_BUG_FALLIBLE_EQOPS
JS_BUG_FALLIBLE_TONUM
JS_BUG_WITH_CLOSURE
JS_BUG_SET_ENUMERATE

0
1
1
1
1
0
1
1
1
1
0
1

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

new object o: o.p = v sets o[0] */


o[0] defaults to null, not void */
o[""] is equivalent to o[0] */
1 && 1 => true, 1 && 0 => 0 bug */
o.toString() trumps o.valueOf() */
void 0 + 0 == "undefined0" */
eval('this') in function f is f */
Math.eval('sin(x)') vs. local x */
fallible/intransitive equality ops */
fallible ValueToNumber primitive */
with(o)function f(){} sets o.f */
o.p=q flags o.p JSPROP_ENUMERATE */

#define
#define
#define
#define
#define
#define

JS_HAS_PROP_DELETE
JS_HAS_CALL_OBJECT
JS_HAS_LABEL_STATEMENT
JS_HAS_DO_WHILE_LOOP
JS_HAS_SWITCH_STATEMENT
JS_HAS_SOME_PERL_FUN

0
0
0
0
0
1

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

delete o.p removes p from o */


fun.caller is stack frame obj */
has break/continue to label: */
has do {...} while (b) */
has switch (v) {case c: ...} */
has array.join/reverse/sort */

#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define

JS_HAS_MORE_PERL_FUN
JS_HAS_VALUEOF_HINT
JS_HAS_LEXICAL_CLOSURE
JS_HAS_APPLY_FUNCTION
JS_HAS_CALL_FUNCTION
JS_HAS_OBJ_PROTO_PROP
JS_HAS_REGEXPS
JS_HAS_SEQUENCE_OPS
JS_HAS_INITIALIZERS
JS_HAS_OBJ_WATCHPOINT
JS_HAS_EXPORT_IMPORT
JS_HAS_EVAL_THIS_SCOPE
JS_HAS_TRIPLE_EQOPS
JS_HAS_SHARP_VARS
JS_HAS_REPLACE_LAMBDA
JS_HAS_SCRIPT_OBJECT
JS_HAS_XDR
JS_HAS_EXCEPTIONS
JS_HAS_UNDEFINED
JS_HAS_TOSOURCE
JS_HAS_IN_OPERATOR
JS_HAS_INSTANCEOF
JS_HAS_ARGS_OBJECT
JS_HAS_DEBUGGER_KEYWORD
JS_HAS_ERROR_EXCEPTIONS
JS_HAS_CATCH_GUARD
JS_HAS_NEW_OBJ_METHODS
JS_HAS_SPARSE_ARRAYS
JS_HAS_DFLT_MSG_STRINGS
JS_HAS_NUMBER_FORMATS
JS_HAS_GETTER_SETTER
JS_HAS_UNEVAL
JS_HAS_CONST
JS_HAS_FUN_EXPR_STMT
JS_HAS_LVALUE_RETURN

0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0

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

has array.push, str.substr, etc */


valueOf(hint) where hint is typeof */
nested functions, lexically closed */
has apply(fun, arg1, ... argN) */
has fun.call(obj, arg1, ... argN) */
has o.__proto__ etc. */
has perl r.e.s via RegExp, /pat/ */
has array.slice, string.concat */
has var o = {'foo': 42, 'bar':3} */
has o.watch and o.unwatch */
has export fun; import obj.fun */
Math.eval is same as with (Math) */
has === and !== identity eqops */
has #n=, #n# for object literals */
has string.replace(re, lambda) */
has (new Script("x++")).exec() */
has XDR API and object methods */
has exception handling */
has global "undefined" property */
has Object/Array toSource method */
has in operator ('p' in {p:1}) */
has {p:1} instanceof Object */
has minimal ECMA arguments object */
has hook for debugger keyword */
has error object hierarchy */
has exception handling catch guard */
has Object.prototype query methods */
array methods preserve empty elems */
provides English error messages */
numbers have formatting methods */
has JS2 getter/setter functions */
has uneval() top-level function */
has JS2 const as alternative var */
has function expression statement */
has o.item(i) = j; for native item */

#elif JS_VERSION == 120


#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define

JS_BUG_AUTO_INDEX_PROPS
JS_BUG_NULL_INDEX_PROPS
JS_BUG_EMPTY_INDEX_ZERO
JS_BUG_SHORT_CIRCUIT
JS_BUG_EAGER_TOSTRING
JS_BUG_VOID_TOSTRING
JS_BUG_EVAL_THIS_FUN
JS_BUG_EVAL_THIS_SCOPE
JS_BUG_FALLIBLE_EQOPS
JS_BUG_FALLIBLE_TONUM
JS_BUG_WITH_CLOSURE
JS_BUG_SET_ENUMERATE

0
0
0
0
0
1
0
0
0
0
1
1

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

new object o: o.p = v sets o[0] */


o[0] defaults to null, not void */
o[""] is equivalent to o[0] */
1 && 1 => true, 1 && 0 => 0 bug */
o.toString() trumps o.valueOf() */
void 0 + 0 == "undefined0" */
eval('this') in function f is f */
Math.eval('sin(x)') vs. local x */
fallible/intransitive equality ops */
fallible ValueToNumber primitive */
with(o)function f(){} sets o.f */
o.p=q flags o.p JSPROP_ENUMERATE */

#define
#define
#define
#define
#define
#define
#define
#define
#define

JS_HAS_PROP_DELETE
JS_HAS_CALL_OBJECT
JS_HAS_LABEL_STATEMENT
JS_HAS_DO_WHILE_LOOP
JS_HAS_SWITCH_STATEMENT
JS_HAS_SOME_PERL_FUN
JS_HAS_MORE_PERL_FUN
JS_HAS_VALUEOF_HINT
JS_HAS_LEXICAL_CLOSURE

1
1
1
1
1
1
1
1
1

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

delete o.p removes p from o */


fun.caller is stack frame obj */
has break/continue to label: */
has do {...} while (b) */
has switch (v) {case c: ...} */
has array.join/reverse/sort */
has array.push, str.substr, etc */
valueOf(hint) where hint is typeof */
nested functions, lexically closed */

#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define

JS_HAS_APPLY_FUNCTION
JS_HAS_CALL_FUNCTION
JS_HAS_OBJ_PROTO_PROP
JS_HAS_REGEXPS
JS_HAS_SEQUENCE_OPS
JS_HAS_INITIALIZERS
JS_HAS_OBJ_WATCHPOINT
JS_HAS_EXPORT_IMPORT
JS_HAS_EVAL_THIS_SCOPE
JS_HAS_TRIPLE_EQOPS
JS_HAS_SHARP_VARS
JS_HAS_REPLACE_LAMBDA
JS_HAS_SCRIPT_OBJECT
JS_HAS_XDR
JS_HAS_EXCEPTIONS
JS_HAS_UNDEFINED
JS_HAS_TOSOURCE
JS_HAS_IN_OPERATOR
JS_HAS_INSTANCEOF
JS_HAS_ARGS_OBJECT
JS_HAS_DEBUGGER_KEYWORD
JS_HAS_ERROR_EXCEPTIONS
JS_HAS_CATCH_GUARD
JS_HAS_NEW_OBJ_METHODS
JS_HAS_SPARSE_ARRAYS
JS_HAS_DFLT_MSG_STRINGS
JS_HAS_NUMBER_FORMATS
JS_HAS_GETTER_SETTER
JS_HAS_UNEVAL
JS_HAS_CONST
JS_HAS_FUN_EXPR_STMT
JS_HAS_LVALUE_RETURN

1
0
1
1
1
1
1
1
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0

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

has apply(fun, arg1, ... argN) */


has fun.call(obj, arg1, ... argN) */
has o.__proto__ etc. */
has perl r.e.s via RegExp, /pat/ */
has array.slice, string.concat */
has var o = {'foo': 42, 'bar':3} */
has o.watch and o.unwatch */
has export fun; import obj.fun */
Math.eval is same as with (Math) */
has === and !== identity eqops */
has #n=, #n# for object literals */
has string.replace(re, lambda) */
has (new Script("x++")).exec() */
has XDR API and object methods */
has exception handling */
has global "undefined" property */
has Object/Array toSource method */
has in operator ('p' in {p:1}) */
has {p:1} instanceof Object */
has minimal ECMA arguments object */
has hook for debugger keyword */
has error object hierarchy */
has exception handling catch guard */
has Object.prototype query methods */
array methods preserve empty elems */
provides English error messages */
numbers have formatting methods */
has JS2 getter/setter functions */
has uneval() top-level function */
has JS2 const as alternative var */
has function expression statement */
has o.item(i) = j; for native item */

#elif JS_VERSION == 130


#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define

JS_BUG_AUTO_INDEX_PROPS
JS_BUG_NULL_INDEX_PROPS
JS_BUG_EMPTY_INDEX_ZERO
JS_BUG_SHORT_CIRCUIT
JS_BUG_EAGER_TOSTRING
JS_BUG_VOID_TOSTRING
JS_BUG_EVAL_THIS_FUN
JS_BUG_EVAL_THIS_SCOPE
JS_BUG_FALLIBLE_EQOPS
JS_BUG_FALLIBLE_TONUM
JS_BUG_WITH_CLOSURE
JS_BUG_SET_ENUMERATE

0
0
0
0
0
0
0
0
0
0
1
0

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

new object o: o.p = v sets o[0] */


o[0] defaults to null, not void */
o[""] is equivalent to o[0] */
1 && 1 => true, 1 && 0 => 0 bug */
o.toString() trumps o.valueOf() */
void 0 + 0 == "undefined0" */
eval('this') in function f is f */
Math.eval('sin(x)') vs. local x */
fallible/intransitive equality ops */
fallible ValueToNumber primitive */
with(o)function f(){} sets o.f */
o.p=q flags o.p JSPROP_ENUMERATE */

#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define

JS_HAS_PROP_DELETE
JS_HAS_CALL_OBJECT
JS_HAS_LABEL_STATEMENT
JS_HAS_DO_WHILE_LOOP
JS_HAS_SWITCH_STATEMENT
JS_HAS_SOME_PERL_FUN
JS_HAS_MORE_PERL_FUN
JS_HAS_VALUEOF_HINT
JS_HAS_LEXICAL_CLOSURE
JS_HAS_APPLY_FUNCTION
JS_HAS_CALL_FUNCTION
JS_HAS_OBJ_PROTO_PROP

1
1
1
1
1
1
1
1
1
1
1
1

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

delete o.p removes p from o */


fun.caller is stack frame obj */
has break/continue to label: */
has do {...} while (b) */
has switch (v) {case c: ...} */
has array.join/reverse/sort */
has array.push, str.substr, etc */
valueOf(hint) where hint is typeof */
nested functions, lexically closed */
has apply(fun, arg1, ... argN) */
has fun.call(obj, arg1, ... argN) */
has o.__proto__ etc. */

#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define

JS_HAS_REGEXPS
JS_HAS_SEQUENCE_OPS
JS_HAS_INITIALIZERS
JS_HAS_OBJ_WATCHPOINT
JS_HAS_EXPORT_IMPORT
JS_HAS_EVAL_THIS_SCOPE
JS_HAS_TRIPLE_EQOPS
JS_HAS_SHARP_VARS
JS_HAS_REPLACE_LAMBDA
JS_HAS_SCRIPT_OBJECT
JS_HAS_XDR
JS_HAS_EXCEPTIONS
JS_HAS_UNDEFINED
JS_HAS_TOSOURCE
JS_HAS_IN_OPERATOR
JS_HAS_INSTANCEOF
JS_HAS_ARGS_OBJECT
JS_HAS_DEBUGGER_KEYWORD
JS_HAS_ERROR_EXCEPTIONS
JS_HAS_CATCH_GUARD
JS_HAS_NEW_OBJ_METHODS
JS_HAS_SPARSE_ARRAYS
JS_HAS_DFLT_MSG_STRINGS
JS_HAS_NUMBER_FORMATS
JS_HAS_GETTER_SETTER
JS_HAS_UNEVAL
JS_HAS_CONST
JS_HAS_FUN_EXPR_STMT
JS_HAS_LVALUE_RETURN

1
1
1
1
1
1
1
1
1
1
1
0
1
1
0
0
1
1
0
0
0
0
1
0
0
0
0
0
0

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

has perl r.e.s via RegExp, /pat/ */


has array.slice, string.concat */
has var o = {'foo': 42, 'bar':3} */
has o.watch and o.unwatch */
has export fun; import obj.fun */
Math.eval is same as with (Math) */
has === and !== identity eqops */
has #n=, #n# for object literals */
has string.replace(re, lambda) */
has (new Script("x++")).exec() */
has XDR API and object methods */
has exception handling */
has global "undefined" property */
has Object/Array toSource method */
has in operator ('p' in {p:1}) */
has {p:1} instanceof Object */
has minimal ECMA arguments object */
has hook for debugger keyword */
has error object hierarchy */
has exception handling catch guard */
has Object.prototype query methods */
array methods preserve empty elems */
provides English error messages */
numbers have formatting methods */
has JS2 getter/setter functions */
has uneval() top-level function */
has JS2 const as alternative var */
has function expression statement */
has o.item(i) = j; for native item */

#elif JS_VERSION == 140


#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define

JS_BUG_AUTO_INDEX_PROPS
JS_BUG_NULL_INDEX_PROPS
JS_BUG_EMPTY_INDEX_ZERO
JS_BUG_SHORT_CIRCUIT
JS_BUG_EAGER_TOSTRING
JS_BUG_VOID_TOSTRING
JS_BUG_EVAL_THIS_FUN
JS_BUG_EVAL_THIS_SCOPE
JS_BUG_FALLIBLE_EQOPS
JS_BUG_FALLIBLE_TONUM
JS_BUG_WITH_CLOSURE
JS_BUG_SET_ENUMERATE

0
0
0
0
0
0
0
0
0
0
1
0

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

new object o: o.p = v sets o[0] */


o[0] defaults to null, not void */
o[""] is equivalent to o[0] */
1 && 1 => true, 1 && 0 => 0 bug */
o.toString() trumps o.valueOf() */
void 0 + 0 == "undefined0" */
eval('this') in function f is f */
Math.eval('sin(x)') vs. local x */
fallible/intransitive equality ops */
fallible ValueToNumber primitive */
with(o)function f(){} sets o.f */
o.p=q flags o.p JSPROP_ENUMERATE */

#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define

JS_HAS_PROP_DELETE
JS_HAS_CALL_OBJECT
JS_HAS_LABEL_STATEMENT
JS_HAS_DO_WHILE_LOOP
JS_HAS_SWITCH_STATEMENT
JS_HAS_SOME_PERL_FUN
JS_HAS_MORE_PERL_FUN
JS_HAS_VALUEOF_HINT
JS_HAS_LEXICAL_CLOSURE
JS_HAS_APPLY_FUNCTION
JS_HAS_CALL_FUNCTION
JS_HAS_OBJ_PROTO_PROP
JS_HAS_REGEXPS
JS_HAS_SEQUENCE_OPS
JS_HAS_INITIALIZERS

1
1
1
1
1
1
1
1
1
1
1
1
1
1
1

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

delete o.p removes p from o */


fun.caller is stack frame obj */
has break/continue to label: */
has do {...} while (b) */
has switch (v) {case c: ...} */
has array.join/reverse/sort */
has array.push, str.substr, etc */
valueOf(hint) where hint is typeof */
nested functions, lexically closed */
has apply(fun, arg1, ... argN) */
has fun.call(obj, arg1, ... argN) */
has o.__proto__ etc. */
has perl r.e.s via RegExp, /pat/ */
has array.slice, string.concat */
has var o = {'foo': 42, 'bar':3} */

#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define

JS_HAS_OBJ_WATCHPOINT
JS_HAS_EXPORT_IMPORT
JS_HAS_EVAL_THIS_SCOPE
JS_HAS_TRIPLE_EQOPS
JS_HAS_SHARP_VARS
JS_HAS_REPLACE_LAMBDA
JS_HAS_SCRIPT_OBJECT
JS_HAS_XDR
JS_HAS_EXCEPTIONS
JS_HAS_UNDEFINED
JS_HAS_TOSOURCE
JS_HAS_IN_OPERATOR
JS_HAS_INSTANCEOF
JS_HAS_ARGS_OBJECT
JS_HAS_DEBUGGER_KEYWORD
JS_HAS_ERROR_EXCEPTIONS
JS_HAS_CATCH_GUARD
JS_HAS_NEW_OBJ_METHODS
JS_HAS_SPARSE_ARRAYS
JS_HAS_DFLT_MSG_STRINGS
JS_HAS_NUMBER_FORMATS
JS_HAS_GETTER_SETTER
JS_HAS_UNEVAL
JS_HAS_CONST
JS_HAS_FUN_EXPR_STMT
JS_HAS_LVALUE_RETURN

1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
0
0
0
0
1
0
0
0
0
0
0

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

has o.watch and o.unwatch */


has export fun; import obj.fun */
Math.eval is same as with (Math) */
has === and !== identity eqops */
has #n=, #n# for object literals */
has string.replace(re, lambda) */
has (new Script("x++")).exec() */
has XDR API and object methods */
has exception handling */
has global "undefined" property */
has Object/Array toSource method */
has in operator ('p' in {p:1}) */
has {p:1} instanceof Object */
has minimal ECMA arguments object */
has hook for debugger keyword */
rt errors reflected as exceptions */
has exception handling catch guard */
has Object.prototype query methods */
array methods preserve empty elems */
provides English error messages */
numbers have formatting methods */
has JS2 getter/setter functions */
has uneval() top-level function */
has JS2 const as alternative var */
has function expression statement */
has o.item(i) = j; for native item */

#elif JS_VERSION == 150


#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define

JS_BUG_AUTO_INDEX_PROPS
JS_BUG_NULL_INDEX_PROPS
JS_BUG_EMPTY_INDEX_ZERO
JS_BUG_SHORT_CIRCUIT
JS_BUG_EAGER_TOSTRING
JS_BUG_VOID_TOSTRING
JS_BUG_EVAL_THIS_FUN
JS_BUG_EVAL_THIS_SCOPE
JS_BUG_FALLIBLE_EQOPS
JS_BUG_FALLIBLE_TONUM
JS_BUG_WITH_CLOSURE
JS_BUG_SET_ENUMERATE

0
0
0
0
0
0
0
0
0
0
0
0

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

new object o: o.p = v sets o[0] */


o[0] defaults to null, not void */
o[""] is equivalent to o[0] */
1 && 1 => true, 1 && 0 => 0 bug */
o.toString() trumps o.valueOf() */
void 0 + 0 == "undefined0" */
eval('this') in function f is f */
Math.eval('sin(x)') vs. local x */
fallible/intransitive equality ops */
fallible ValueToNumber primitive */
with(o)function f(){} sets o.f */
o.p=q flags o.p JSPROP_ENUMERATE */

#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define

JS_HAS_PROP_DELETE
JS_HAS_CALL_OBJECT
JS_HAS_LABEL_STATEMENT
JS_HAS_DO_WHILE_LOOP
JS_HAS_SWITCH_STATEMENT
JS_HAS_SOME_PERL_FUN
JS_HAS_MORE_PERL_FUN
JS_HAS_VALUEOF_HINT
JS_HAS_LEXICAL_CLOSURE
JS_HAS_APPLY_FUNCTION
JS_HAS_CALL_FUNCTION
JS_HAS_OBJ_PROTO_PROP
JS_HAS_REGEXPS
JS_HAS_SEQUENCE_OPS
JS_HAS_INITIALIZERS
JS_HAS_OBJ_WATCHPOINT
JS_HAS_EXPORT_IMPORT
JS_HAS_EVAL_THIS_SCOPE

1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1

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

delete o.p removes p from o */


fun.caller is stack frame obj */
has break/continue to label: */
has do {...} while (b) */
has switch (v) {case c: ...} */
has array.join/reverse/sort */
has array.push, str.substr, etc */
valueOf(hint) where hint is typeof */
nested functions, lexically closed */
has apply(fun, arg1, ... argN) */
has fun.call(obj, arg1, ... argN) */
has o.__proto__ etc. */
has perl r.e.s via RegExp, /pat/ */
has array.slice, string.concat */
has var o = {'foo': 42, 'bar':3} */
has o.watch and o.unwatch */
has export fun; import obj.fun */
Math.eval is same as with (Math) */

#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define

JS_HAS_TRIPLE_EQOPS
JS_HAS_SHARP_VARS
JS_HAS_REPLACE_LAMBDA
JS_HAS_SCRIPT_OBJECT
JS_HAS_XDR
JS_HAS_EXCEPTIONS
JS_HAS_UNDEFINED
JS_HAS_TOSOURCE
JS_HAS_IN_OPERATOR
JS_HAS_INSTANCEOF
JS_HAS_ARGS_OBJECT
JS_HAS_DEBUGGER_KEYWORD
JS_HAS_ERROR_EXCEPTIONS
JS_HAS_CATCH_GUARD
JS_HAS_NEW_OBJ_METHODS
JS_HAS_SPARSE_ARRAYS
JS_HAS_DFLT_MSG_STRINGS
JS_HAS_NUMBER_FORMATS
JS_HAS_GETTER_SETTER
JS_HAS_UNEVAL
JS_HAS_CONST
JS_HAS_FUN_EXPR_STMT
JS_HAS_LVALUE_RETURN

1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
0
1
1
1
1
1
1
1

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

has === and !== identity eqops */


has #n=, #n# for object literals */
has string.replace(re, lambda) */
has (new Script("x++")).exec() */
has XDR API and object methods */
has exception handling */
has global "undefined" property */
has Object/Array toSource method */
has in operator ('p' in {p:1}) */
has {p:1} instanceof Object */
has minimal ECMA arguments object */
has hook for debugger keyword */
rt errors reflected as exceptions */
has exception handling catch guard */
has Object.prototype query methods */
array methods preserve empty elems */
provides English error messages */
numbers have formatting methods */
has JS2 getter/setter functions */
has uneval() top-level function */
has JS2 const as alternative var */
has function expression statement */
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
printf("#define
} else {
printf("#define
printf("#undef
}

{
IS_LITTLE_ENDIAN\n");
IS_BIG_ENDIAN 1\n\n");
IS_LITTLE_ENDIAN 1\n");
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
printf("#define
} else {
printf("#define
printf("#undef
}

{
IS_LITTLE_ENDIAN\n");
IS_BIG_ENDIAN 1\n\n");
IS_LITTLE_ENDIAN 1\n");
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
sizeof_short
sizeof_int
sizeof_int64
sizeof_long
sizeof_float
sizeof_double
sizeof_word
sizeof_dword

=
=
=
=
=
=
=
=
=

PR_BYTES_PER_BYTE;
PR_BYTES_PER_SHORT;
PR_BYTES_PER_INT;
PR_BYTES_PER_INT64;
PR_BYTES_PER_LONG;
PR_BYTES_PER_FLOAT;
PR_BYTES_PER_DOUBLE;
PR_BYTES_PER_WORD;
PR_BYTES_PER_DWORD;

bits_per_int64_log2 = PR_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

=
=
=
=
=
=
=
=

PR_ALIGN_OF_SHORT;
PR_ALIGN_OF_INT;
PR_ALIGN_OF_LONG;
PR_ALIGN_OF_INT64;
PR_ALIGN_OF_FLOAT;
PR_ALIGN_OF_DOUBLE;
PR_ALIGN_OF_POINTER;
PR_ALIGN_OF_WORD;

#else /* !CROSS_COMPILE */
if (sizeof(long) == 8) {
do64();
} else {
do32();
}
sizeof_char
sizeof_short
sizeof_int
sizeof_int64
sizeof_long
sizeof_float
sizeof_double
sizeof_word
sizeof_dword

=
=
=
=
=
=
=
=
=

sizeof(char);
sizeof(short);
sizeof(int);
8;
sizeof(long);
sizeof(float);
sizeof(double);
sizeof(prword);
8;

bits_per_int64_log2 = 6;
align_of_short
align_of_int
align_of_long
if (sizeof(INT64) <
/* this machine
align_of_int64
} else {
align_of_int64
}
align_of_float
align_of_double
align_of_pointer
align_of_word

= ALIGN_OF(short);
= ALIGN_OF(int);
= ALIGN_OF(long);
8) {
doesn't actually support int64's */
= ALIGN_OF(fakelonglong);
= ALIGN_OF(int64);
=
=
=
=

ALIGN_OF(float);
ALIGN_OF(double);
ALIGN_OF(pointer);
ALIGN_OF(prword);

#endif /* CROSS_COMPILE */
printf("#define
printf("#define
printf("#define
printf("#define
printf("#define
printf("#define
printf("#define
printf("#define
printf("#define
printf("\n");

JS_BYTES_PER_BYTE
JS_BYTES_PER_SHORT
JS_BYTES_PER_INT
JS_BYTES_PER_INT64
JS_BYTES_PER_LONG
JS_BYTES_PER_FLOAT
JS_BYTES_PER_DOUBLE
JS_BYTES_PER_WORD
JS_BYTES_PER_DWORD

%dL\n",
%dL\n",
%dL\n",
%dL\n",
%dL\n",
%dL\n",
%dL\n",
%dL\n",
%dL\n",

sizeof_char);
sizeof_short);
sizeof_int);
sizeof_int64);
sizeof_long);
sizeof_float);
sizeof_double);
sizeof_word);
sizeof_dword);

printf("#define
printf("#define
printf("#define
printf("#define
printf("#define
printf("#define
printf("#define
printf("#define
printf("\n");

JS_BITS_PER_BYTE
JS_BITS_PER_SHORT
JS_BITS_PER_INT
JS_BITS_PER_INT64
JS_BITS_PER_LONG
JS_BITS_PER_FLOAT
JS_BITS_PER_DOUBLE
JS_BITS_PER_WORD

%dL\n",
%dL\n",
%dL\n",
%dL\n",
%dL\n",
%dL\n",
%dL\n",
%dL\n",

bpb);
bpb *
bpb *
bpb *
bpb *
bpb *
bpb *
bpb *

printf("#define
printf("#define
printf("#define
printf("#define
printf("#define
printf("#define
printf("#define
printf("#define
printf("\n");

JS_BITS_PER_BYTE_LOG2
JS_BITS_PER_SHORT_LOG2
JS_BITS_PER_INT_LOG2
JS_BITS_PER_INT64_LOG2
JS_BITS_PER_LONG_LOG2
JS_BITS_PER_FLOAT_LOG2
JS_BITS_PER_DOUBLE_LOG2
JS_BITS_PER_WORD_LOG2

printf("#define
printf("#define
printf("#define
printf("#define
printf("#define
printf("#define
printf("#define
printf("#define
printf("\n");

JS_ALIGN_OF_SHORT
JS_ALIGN_OF_INT
JS_ALIGN_OF_LONG
JS_ALIGN_OF_INT64
JS_ALIGN_OF_FLOAT
JS_ALIGN_OF_DOUBLE
JS_ALIGN_OF_POINTER
JS_ALIGN_OF_WORD

%dL\n",
%dL\n",
%dL\n",
%dL\n",
%dL\n",
%dL\n",
%dL\n",
%dL\n",

%dL\n",
%dL\n",
%dL\n",
%dL\n",
%dL\n",
%dL\n",
%dL\n",
%dL\n",

sizeof_short);
sizeof_int);
sizeof_int64);
sizeof_long);
sizeof_float);
sizeof_double);
sizeof_word);

Log2(bpb));
Log2(bpb * sizeof_short));
Log2(bpb * sizeof_int));
bits_per_int64_log2);
Log2(bpb * sizeof_long));
Log2(bpb * sizeof_float));
Log2(bpb * sizeof_double));
Log2(bpb * sizeof_word));

align_of_short);
align_of_int);
align_of_long);
align_of_int64);
align_of_float);
align_of_double);
align_of_pointer);
align_of_word);

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_wo
rd));
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
#define
#define
#define
#define
#define
#define
#define
#define

JS_BYTES_PER_BYTE
JS_BYTES_PER_SHORT
JS_BYTES_PER_INT
JS_BYTES_PER_INT64
JS_BYTES_PER_LONG
JS_BYTES_PER_FLOAT
JS_BYTES_PER_DOUBLE
JS_BYTES_PER_WORD
JS_BYTES_PER_DWORD

1L
2L
4L
8L
4L
4L
8L
4L
8L

#define
#define
#define
#define
#define
#define
#define
#define

JS_BITS_PER_BYTE
JS_BITS_PER_SHORT
JS_BITS_PER_INT
JS_BITS_PER_INT64
JS_BITS_PER_LONG
JS_BITS_PER_FLOAT
JS_BITS_PER_DOUBLE
JS_BITS_PER_WORD

8L
16L
32L
64L
32L
32L
64L
32L

#define
#define
#define
#define
#define
#define
#define
#define

JS_BITS_PER_BYTE_LOG2
JS_BITS_PER_SHORT_LOG2
JS_BITS_PER_INT_LOG2
JS_BITS_PER_INT64_LOG2
JS_BITS_PER_LONG_LOG2
JS_BITS_PER_FLOAT_LOG2
JS_BITS_PER_DOUBLE_LOG2
JS_BITS_PER_WORD_LOG2

#define
#define
#define
#define
#define
#define
#define
#define

JS_ALIGN_OF_SHORT
JS_ALIGN_OF_INT
JS_ALIGN_OF_LONG
JS_ALIGN_OF_INT64
JS_ALIGN_OF_FLOAT
JS_ALIGN_OF_DOUBLE
JS_ALIGN_OF_POINTER
JS_ALIGN_OF_WORD

3L
4L
5L
6L
5L
5L
6L
5L

2L
4L
4L
2L
4L
4L
4L
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
#define
#define
#define
#define
#define
#define
#define
#define

JS_BYTES_PER_BYTE
JS_BYTES_PER_SHORT
JS_BYTES_PER_INT
JS_BYTES_PER_INT64
JS_BYTES_PER_LONG
JS_BYTES_PER_FLOAT
JS_BYTES_PER_DOUBLE
JS_BYTES_PER_WORD
JS_BYTES_PER_DWORD

1L
2L
4L
8L
4L
4L
8L
4L
8L

#define
#define
#define
#define
#define
#define
#define
#define

JS_BITS_PER_BYTE
JS_BITS_PER_SHORT
JS_BITS_PER_INT
JS_BITS_PER_INT64
JS_BITS_PER_LONG
JS_BITS_PER_FLOAT
JS_BITS_PER_DOUBLE
JS_BITS_PER_WORD

8L
16L
32L
64L
32L
32L
64L
32L

#define
#define
#define
#define
#define
#define
#define
#define

JS_BITS_PER_BYTE_LOG2
JS_BITS_PER_SHORT_LOG2
JS_BITS_PER_INT_LOG2
JS_BITS_PER_INT64_LOG2
JS_BITS_PER_LONG_LOG2
JS_BITS_PER_FLOAT_LOG2
JS_BITS_PER_DOUBLE_LOG2
JS_BITS_PER_WORD_LOG2

#define JS_ALIGN_OF_SHORT
#define JS_ALIGN_OF_INT
#define JS_ALIGN_OF_LONG

2L
4L
4L

3L
4L
5L
6L
5L
5L
6L
5L

#define
#define
#define
#define
#define

JS_ALIGN_OF_INT64
JS_ALIGN_OF_FLOAT
JS_ALIGN_OF_DOUBLE
JS_ALIGN_OF_POINTER
JS_ALIGN_OF_WORD

8L
4L
4L
4L
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
#define
#define
#define
#define
#define
#define
#define
#define

JS_BYTES_PER_BYTE
JS_BYTES_PER_SHORT
JS_BYTES_PER_INT
JS_BYTES_PER_INT64
JS_BYTES_PER_LONG
JS_BYTES_PER_FLOAT
JS_BYTES_PER_DOUBLE
JS_BYTES_PER_WORD
JS_BYTES_PER_DWORD

1L
2L
2L
8L
4L
4L
8L
4L
8L

#define
#define
#define
#define
#define
#define
#define
#define

JS_BITS_PER_BYTE
JS_BITS_PER_SHORT
JS_BITS_PER_INT
JS_BITS_PER_INT64
JS_BITS_PER_LONG
JS_BITS_PER_FLOAT
JS_BITS_PER_DOUBLE
JS_BITS_PER_WORD

8L
16L
16L
64L
32L
32L
64L
32L

#define
#define
#define
#define
#define
#define
#define
#define

JS_BITS_PER_BYTE_LOG2
JS_BITS_PER_SHORT_LOG2
JS_BITS_PER_INT_LOG2
JS_BITS_PER_INT64_LOG2
JS_BITS_PER_LONG_LOG2
JS_BITS_PER_FLOAT_LOG2
JS_BITS_PER_DOUBLE_LOG2
JS_BITS_PER_WORD_LOG2

#define
#define
#define
#define
#define
#define
#define
#define

JS_ALIGN_OF_SHORT
JS_ALIGN_OF_INT
JS_ALIGN_OF_LONG
JS_ALIGN_OF_INT64
JS_ALIGN_OF_FLOAT
JS_ALIGN_OF_DOUBLE
JS_ALIGN_OF_POINTER
JS_ALIGN_OF_WORD

3L
4L
4L
6L
5L
5L
6L
5L

2L
2L
2L
2L
2L
2L
2L
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*

"jsstddef.h"
<ctype.h>
<math.h>
<stdlib.h>
<string.h>
"jstypes.h"
"jsprf.h"
"prmjtime.h"
"jsutil.h" /* Added by JSIFY */
"jsapi.h"
"jsconfig.h"
"jscntxt.h"
"jsdate.h"
"jsinterp.h"
"jsnum.h"
"jsobj.h"
"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
#define
#define
#define
#define
#define
#define

HalfTimeDomain 8.64e15
HoursPerDay
24.0
MinutesPerDay (HoursPerDay * MinutesPerHour)
MinutesPerHour 60.0
SecondsPerDay (MinutesPerDay * SecondsPerMinute)
SecondsPerHour (MinutesPerHour * SecondsPerMinute)
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,
{"parse",
date_parse,
{0,0,0,0,0}
};

MAXARGS,0,0 },
1,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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

"jsstddef.h"
<string.h>
"jstypes.h"
"jsutil.h" /* Added by JSIFY */
"jsclist.h"
"jsapi.h"
"jscntxt.h"
"jsconfig.h"
"jsdbgapi.h"
"jsfun.h"
"jsgc.h"
"jsinterp.h"
"jslock.h"
"jsobj.h"
"jsopcode.h"
"jsscope.h"
"jsscript.h"
"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;
jsval
userid;
JSScopeProperty
*sprop;
JSPropertyOp
setter;
JSWatchPointHandler handler;
void
*closure;
jsrefcount
nrefs;
} JSWatchPoint;

/* weak link, see js_FinalizeObject */

#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);
return JS_FALSE;

/* XXX can't happen */

}
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
/* XXX
nbytes
return

don't count sym_id, assume it's shared */


don't worry about aliases (extra symbols for an sprop) */
+= sizeof *sym;
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
#define
#define
#define
#define
#define

JSPD_ENUMERATE
JSPD_READONLY
JSPD_PERMANENT
JSPD_ALIAS
JSPD_ARGUMENT
JSPD_VARIABLE

0x01
0x02
0x04
0x08
0x10
0x20

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

visible to for/in loop */


assignment is error */
property cannot be deleted */
property has an alias id */
argument to function */
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)
#else
# define METER(x)
#endif

x
/* nothing */

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,
fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,

"Double hashing statistics:\n");


"
table size (in entries): %u\n",
"
number of entries: %u\n",
" number of removed entries: %u\n",
"
number of searches: %u\n",
"
number of hits: %u\n",
"
number of misses: %u\n",
"
mean steps per search: %g\n",

fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,

"
mean hash chain length: %g\n",
"
standard deviation: %g\n",
" maximum hash chain length: %u\n",
"
number of lookups: %u\n",
" adds that made a new entry: %u\n",
" adds that found an entry: %u\n",
"
add failures: %u\n",
"
useful removes: %u\n",
"
useless removes: %u\n",
" removes while enumerating: %u\n",
"
number of grows: %u\n",
"
number of shrinks: %u\n",

if (maxChainLen && hash2) {


fputs("Maximum hash chain:\n", fp);
hash1 = maxChainHash1;

tableSize);
table->entryCount);
table->removedCount);
table->stats.searches);
table->stats.hits);
table->stats.misses);
(double)table->stats.steps
/ table->stats.searches);
mean);
sigma);
maxChainLen);
table->stats.lookups);
table->stats.addMisses);
table->stats.addHits);
table->stats.addFailures);
table->stats.removeHits);
table->stats.removeMisses);
table->stats.removeEnums);
table->stats.grows);
table->stats.shrinks);

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
typedef uint32
typedef struct JSDHashEntryHdr
typedef struct JSDHashEntryStub
typedef struct JSDHashTable
typedef struct JSDHashTableOps

typedefs. */
JSDHashNumber;
JSDHashEntryHdr;
JSDHashEntryStub;
JSDHashTable;
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)
#define JS_DHASH_ENTRY_IS_BUSY(entry)

((entry)->keyHash == 0)
(!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
uint32
uint32
uint32
uint32
uint32
uint32
uint32
uint32
uint32
uint32
uint32
uint32
} stats;
#endif
};

searches;
steps;
hits;
misses;
lookups;
addMisses;
addHits;
addFailures;
removeHits;
removeMisses;
removeEnums;
grows;
shrinks;

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

total number of table searches */


hash chain links traversed */
searches that found key */
searches that didn't find key */
number of JS_DHASH_LOOKUPs */
adds that miss, and do work */
adds that hit an existing entry */
out-of-memory during add growth */
removes that hit, and do work */
useless removes that miss */
removes done by Enumerate */
table expansions */
table contractions */

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

DBL_DIG 15
DBL_MAX_10_EXP 308
DBL_MAX_EXP 1024
FLT_RADIX 2
FLT_ROUNDS 1
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
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#ifndef
#define
#endif

Exp_shift 20
Exp_shift1 20
Exp_msk1
0x100000
Exp_msk11 0x100000
Exp_mask 0x7ff00000
P 53
Bias 1023
Emin (-1022)
Exp_1 0x3ff00000
Exp_11 0x3ff00000
Ebits 11
Frac_mask 0xfffff
Frac_mask1 0xfffff
Ten_pmax 22
Bletch 0x10
Bndry_mask 0xfffff
Bndry_mask1 0xfffff
LSB 1
Sign_bit 0x80000000
Log2P 1
Tiny0 0
Tiny1 1
Quick_max 14
Int_max 14
Infinite(x) (word0(x) == 0x7ff00000) /* sufficient test for here */
NO_IEEE_Scale
Avoid_Underflow

#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;
int32 k;
int32 maxwds;
int32 sign;
igint routines! */
int32 wds;
ost significant word must
ULong x[1];
};

/*
/*
/*
/*

Free list link */


lg2(maxwds) */
Number of words allocated for x */
Zero if positive, 1 if negative. Ignored by most B

/* Actual number of words. If value is nonzero, the m


be nonzero. */
/* 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 65
535 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 d
ouble 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 significa
nt
* 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
if (e1
e1
if

/= tens[i];
&= ~15) {
>>= 4;
(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);
bs = i2b(1);

/* rv = bb * 2^bbe */

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 mus
t 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 mu
st 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 exa
mple,
* 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 ex
pected. */
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 o
dd 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 ov
erflows. */
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
/*
ds
if

>= 0 && k <= Int_max) {


Yes. */
= tens[k];
(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,
}
/* Now we have d/10^k =
(mhi * 2^m2) / (2^s2
ant digit, divided by 10^k.

b5);
(b * 2^b2) / (2^s2 * 5^s5) and
* 5^s5) = one-half of last printed or input signific
*/

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 las
t input
significant digit instead of one half of it when the decimal outp
ut 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 wor
d. */
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 i
s closer to d. */
if (ilim < 0 || (i = cmp(b,S = multadd(S,5,0))) < 0 || (i == 0 && !biasU
p)) {
/* 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 l
ess than d. */
/* mhi/S = maximum acceptable error, divided by 10^k, if the output is g
reater 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 signific
ant 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
ble d)
{
int decPt;
igit returned by JS_dtoa */
int sign;
int nDigits;
toa */
char *numBegin = buffer+2;
e +2 leaves space for */

bufferSize, JSDToStrMode mode, int precision, dou


/* Position of decimal point relative to first d
/* Nonzero if the sign bit was set in d */
/* Number of significand digits returned by JS_d
/* Pointer to the digits returned by JS_dtoa; th

/* the sign and/or decimal point */


/* Pointer past the digits returned by JS_dtoa *

char *numEnd;
/

JS_ASSERT(bufferSize >= (size_t)(mode <= DTOSTR_STANDARD_EXPONENTIAL ? DTOST


R_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 th
e buffer may not be large enough to hold a large integer. */
if (!JS_dtoa(d, dtoaModes[mode], mode >= DTOSTR_FIXED, precision, &decPt, &s
ign, &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 requ
ired 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 t
hat we could produce,
* which occurs when printing -5e-324 in binary. We could compute a better esti
mate of the size of
* the output string and malloc fewer bytes depending on d and base, but why bot
her? */
#define DTOBASESTR_BUFFER_SIZE 1078
#define BASEDIGIT(digit) ((char)(((digit) >= 10) ? 'a' - 10 + (digit) : '0' + (d
igit)))
JS_FRIEND_API(char *)
JS_dtobasestr(int base,
{
char *buffer;
char *p;
char *pInt;
string */
char *q;
uint32 digit;
double di;
double df;

double d)
/* The output string */
/* Pointer to current position in the buffer */
/* Pointer to the beginning of the integer part of the

/* d truncated to an integer */
/* 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 routine
s 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 s
tring'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 sign
ificant 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 res
ult
* will be identical to the original number (except that, due to ECMA, -0 will g
et converted
* to +0). These round-trip modes return the minimum number of significand digi
ts 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-t
rip */
DTOSTR_STANDARD_EXPONENTIAL, /* Always exponential format; round-trip */
DTOSTR_FIXED,
/* Round to <precision> digits after the decim
al point; exponential if number is large */
DTOSTR_EXPONENTIAL,
/* Always exponential format; <precision> sign
ificant digits */
DTOSTR_PRECISION
/* Either fixed or exponential format; <precis
ion> 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.2345678
901234567e+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 resultin
g 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, dou
ble dval);
/*
* Convert d to a string in the given base. The integral part of d will be prin
ted 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 wh
ile 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 routin
e 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" warnin
g
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

<string.h>
"jstypes.h"
"jsarena.h" /* Added by JSIFY */
"jsutil.h" /* Added by JSIFY */
"jsbit.h"
"jsprf.h"
"jsapi.h"
"jsatom.h"
"jscntxt.h"
"jsconfig.h"
"jsemit.h"
"jsfun.h"
"jsnum.h"
"jsopcode.h"
"jsparse.h"
"jsscan.h"
"jsscope.h"
"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
if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex,
return JS_FALSE;
beq = 0;
} else {
if (!js_EmitTree(cx, cg, pn2->pn_kid2))
return JS_FALSE;
if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex,
(ptrdiff_t)(CG_OFFSET(cg)
return JS_FALSE;
}
beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0);
if (beq < 0)
return JS_FALSE;
}

notes. */
0, 0))

0,
- top))) {

}
/* 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 ]
name Object
pushobj
newinit
exception
initcatchvar <atom>
enterwith
[< catchguard code >]

only if 2nd+ catch block

if there's a catchguard

* [ifeq <offset to next catch block>]


* < catch block contents >
* leavewith
* goto <end of catch blocks>
*
* If there's no catch block without a
* <offset to next catch block> points
* code will GOSUB to the finally code
* also used for the catch-all trynote
* thrown from catch{} blocks.
*/
for (;;) {
JSStmtInfo stmtInfo2;
JSParseNode *disc;
ptrdiff_t guardnote;

" "
non-local; finally applies
catchguard, the last
to rethrow code. This
if appropriate, and is
for capturing exceptions

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

"jsstddef.h"
"jstypes.h"
"jsatom.h"
"jsopcode.h"
"jsprvtd.h"
"jspubtd.h"

JS_BEGIN_EXTERN_C
typedef enum JSStmtType
STMT_BLOCK
=
STMT_LABEL
=
STMT_IF
=
STMT_ELSE
=
STMT_SWITCH
=
STMT_WITH
=
STMT_TRY
=
STMT_CATCH
=
STMT_FINALLY
=
STMT_DO_LOOP
=
STMT_FOR_LOOP
=
STMT_FOR_IN_LOOP =
STMT_WHILE_LOOP =
} JSStmtType;

{
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12

#define STMT_IS_LOOP(stmt)

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

compound statement: { s1[;... sN] } */


labeled statement: L: s */
if (then) statement */
else clause of if statement */
switch statement */
with statement */
try statement */
catch block */
finally statement */
do/while loop statement */
for loop statement */
for/in loop statement */
while loop statement */

((stmt)->type >= STMT_DO_LOOP)

typedef struct JSStmtInfo JSStmtInfo;


struct JSStmtInfo {
JSStmtType
type;
ptrdiff_t
top;
ptrdiff_t
update;
ptrdiff_t
breaks;
ptrdiff_t
continues;
ptrdiff_t
gosub;
ptrdiff_t
catchJump;

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

statement type */
offset of loop top from cg base */
loop update offset (top if none) */
offset of last break in loop */
offset of last continue in loop */
offset of last GOSUB for this finally */
offset of last end-of-catch jump */

JSAtom
JSStmtInfo

*label;
*down;

/* name of LABEL or CATCH var */


/* 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 {
uint32
flags;
uint32
tryCount;
JSStmtInfo
*topStmt;
JSAtomList
decls;
JSParseNode
*nodeList;
};
#define
#define
#define
#define
#define
#define
#define
#define
#define

TCF_COMPILING
TCF_IN_FUNCTION
TCF_RETURN_EXPR
TCF_RETURN_VOID
TCF_IN_FOR_INIT
TCF_FUN_CLOSURE_VS_VAR
TCF_FUN_USES_NONLOCALS
TCF_FUN_HEAVYWEIGHT
TCF_FUN_FLAGS

0x01
0x02
0x04
0x08
0x10
0x20
0x40
0x80
0xE0

\
\

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

tree context for semantic checks */


statement state flags, see below */
total count of try statements parsed */
top of statement info stack */
function, const, and var declarations */
list of recyclable parse-node structs */

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

generating bytecode; this tc is a cg */


parsing inside function body */
function has 'return expr;' */
function has 'return;' */
parsing init expr of for; exclude 'in' */
function and var with same name */
function refers to non-local names */
function needs Call object per call */
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;
void
*codeMark;
void
*noteMark;
void
*tempMark;
struct {
jsbytecode *base;
jsbytecode *limit;
jsbytecode *next;
} prolog, main, *current;
const char
*filename;
uintN
firstLine;
uintN
currentLine;
JSPrincipals
*principals;
JSAtomList
atomList;
intN
stackDepth;
uintN
maxStackDepth;
jssrcnote
*notes;
uintN
noteCount;
uintN
noteMask;
ptrdiff_t
lastNoteOffset;
JSTryNote
*tryBase;
JSTryNote
*tryNext;
size_t
tryNoteSpace;
};
#define CG_BASE(cg)

/*
/*
/*
/*

base state: statement info stack, etc. */


low watermark in cx->codePool */
low watermark in cx->notePool */
low watermark in cx->tempPool */

/* base of JS bytecode vector */


/* one byte beyond end of bytecode */
/* pointer to next free bytecode */
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*

null or weak link to source filename */


first line, for js_NewScriptFromCG */
line number for tree-based srcnote gen */
principals for constant folding eval */
literals indexed for mapping */
current stack depth in script frame */
maximum stack depth so far */
source notes, see below */
number of source notes so far */
growth increment for notes */
code offset for last source note */
first exception handling note */
next available note */
# of bytes allocated at tryBase */

((cg)->current->base)

#define
#define
#define
#define

CG_LIMIT(cg)
CG_NEXT(cg)
CG_CODE(cg,offset)
CG_OFFSET(cg)

((cg)->current->limit)
((cg)->current->next)
(CG_BASE(cg) + (offset))
PTRDIFF(CG_NEXT(cg), CG_BASE(cg), jsbytecode)

#define
#define
#define
#define
#define

CG_PROLOG_BASE(cg)
CG_PROLOG_LIMIT(cg)
CG_PROLOG_NEXT(cg)
CG_PROLOG_CODE(cg,poff)
CG_PROLOG_OFFSET(cg)

((cg)->prolog.base)
((cg)->prolog.limit)
((cg)->prolog.next)
(CG_PROLOG_BASE(cg) + (poff))
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
SRC_SETLINE
SRC_XDELTA
} JSSrcNoteType;
#define
#define
#define
#define
#define
#define

= 22,
= 23,
= 24

SN_TYPE_BITS
SN_DELTA_BITS
SN_XDELTA_BITS
SN_TYPE_MASK
SN_DELTA_MASK
SN_XDELTA_MASK

#define SN_MAKE_NOTE(sn,t,d)
#define SN_MAKE_XDELTA(sn,d)

#define SN_IS_XDELTA(sn)
#define SN_TYPE(sn)
#define SN_SET_TYPE(sn,type)
#define SN_IS_GETTABLE(sn)

/* bytecode follows a source newline */


/* a file-absolute source line number note */
/* 24-31 are for extended delta notes */
5
3
6
(JS_BITMASK(SN_TYPE_BITS) << SN_DELTA_BITS)
((ptrdiff_t)JS_BITMASK(SN_DELTA_BITS))
((ptrdiff_t)JS_BITMASK(SN_XDELTA_BITS))
(*(sn) = (jssrcnote)
(((t) << SN_DELTA_BITS)
| ((d) & SN_DELTA_MASK)))
(*(sn) = (jssrcnote)
((SRC_XDELTA << SN_DELTA_BITS)
| ((d) & SN_XDELTA_MASK)))
((*(sn) >> SN_DELTA_BITS) >= SRC_XDELTA)
(SN_IS_XDELTA(sn) ? SRC_XDELTA
: *(sn) >> SN_DELTA_BITS)
SN_MAKE_NOTE(sn, type, SN_DELTA(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
#define SN_XDELTA_LIMIT

\
\
\
\

\
\
\
\

((ptrdiff_t)JS_BIT(SN_DELTA_BITS))
((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)
#define SN_NEXT(sn)

((js_SrcNoteArity[SN_TYPE(sn)] == 0) ? 1
: js_SrcNoteLength(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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

<string.h>
"jsstddef.h"
"jstypes.h"
"jsutil.h" /* Added by JSIFY */
"jsprf.h"
"jsapi.h"
"jscntxt.h"
"jsconfig.h"
"jsexn.h"
"jsfun.h"
"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
static
static
static

consider adding rt->atomState.messageAtom */


char js_message_str[] = "message";
char js_filename_str[] = "fileName";
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_EnumerateStub, JS_ResolveStub,
NULL,
NULL,
NULL,
NULL,
};

JS_PropertyStub,
JS_ConvertStub,
NULL,
NULL,

JS_PropertyStub,
exn_finalize,
Exception,
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(j
schar));
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
{ JSEXN_NONE,
{ JSEXN_ERR,
{ JSEXN_ERR,
{ JSEXN_ERR,
{ JSEXN_ERR,
{ JSEXN_ERR,
{ JSEXN_ERR,
{ JSEXN_ERR,
{0,0,NULL}
};

exceptions[] = {
js_Error_str,
js_InternalError_str,
js_EvalError_str,
js_RangeError_str,
js_ReferenceError_str,
js_SyntaxError_str,
js_TypeError_str,
js_URIError_str,

Error },
InternalError },
EvalError },
RangeError },
ReferenceError },
SyntaxError },
TypeError },
URIError },

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,
#endif
{js_toString_str, exn_toString,
{0,0,0,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
#include
#include
#include

"jsscript.h"
"jsstr.h"
"jsutil.h" /* Added by JSIFY */
<string.h>

/* NSPR dependencies */
#include "prio.h"
#include "prerror.h"
#define
#define
#define
#define
#define

SPECIAL_FILE_STRING
CURRENTDIR_PROPERTY
SEPARATOR_PROPERTY
FILE_CONSTRUCTOR
PIPE_SYMBOL

"Special File"
"currentDir"
"separator"
"File"
'|'

#define ASCII
#define UTF8
#define UCS2

0
1
2

#define asciistring
#define utfstring
#define unicodestring

"text"
"binary"
"unicode"

#define
#define
#define
#define
#define

1024
256
32
256
"file://"

MAX_PATH_LENGTH
MODE_SIZE
NUMBER_SIZE
MAX_LINE_LENGTH
URL_PREFIX

#define STDINPUT_NAME
#define STDOUTPUT_NAME
#define STDERROR_NAME

"Standard input stream"


"Standard output stream"
"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, appe
nd, create, etc.. */
int32
type;
/* Asciiz, utf, unicode */
char
byteBuffer[3]; /* bytes read in advance by js_FileRead ( UTF8 e
ncoding ) */
jsint
nbBytesInBuf; /* number of bytes stored in the buffer above */
jschar
charBuffer;
/* character read in advance by readln ( mac fil
es only ) */
JSBool
charBufferUsed; /* flag indicating if the buffer above is being
used */
JSBool
hasRandomAccess; /* can the file be randomly accessed? false f
or 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 ty
pe */
/* 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, j
sval *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
*/
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define

defines and macros


ONE_OCTET_BASE
ONE_OCTET_MASK
CONTINUING_OCTET_BASE
CONTINUING_OCTET_MASK
TWO_OCTET_BASE
TWO_OCTET_MASK
THREE_OCTET_BASE
THREE_OCTET_MASK
FOUR_OCTET_BASE
FOUR_OCTET_MASK
FIVE_OCTET_BASE
FIVE_OCTET_MASK
SIX_OCTET_BASE
SIX_OCTET_MASK
IS_UTF8_1ST_OF_1(x)
IS_UTF8_1ST_OF_2(x)
IS_UTF8_1ST_OF_3(x)
IS_UTF8_1ST_OF_4(x)
IS_UTF8_1ST_OF_5(x)
IS_UTF8_1ST_OF_6(x)

((
((
((
((
((
((

0x00
0x7F
0x80
0x3F
0xC0
0x1F
0xE0
0x0F
0xF0
0x07
0xF8
0x03
0xFC
0x01

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

0xxxxxxx
x1111111
10xxxxxx
00111111
110xxxxx
00011111
1110xxxx
00001111
11110xxx
00000111
111110xx
00000011
1111110x
00000001

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

(x)&~ONE_OCTET_MASK )
(x)&~TWO_OCTET_MASK )
(x)&~THREE_OCTET_MASK)
(x)&~FOUR_OCTET_MASK )
(x)&~FIVE_OCTET_MASK )
(x)&~SIX_OCTET_MASK )

==
==
==
==
==
==

ONE_OCTET_BASE)
TWO_OCTET_BASE)
THREE_OCTET_BASE)
FOUR_OCTET_BASE)
FIVE_OCTET_BASE)
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
#define
#define
#define

MAX_UCS2
DEFAULT_CHAR
BYTE_MASK
BYTE_MARK

0xFFFF
0x003F /* Default char is "?" */
0xBF
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) {
} else if (onechar < 0x800) {
} else if (onechar <= MAX_UCS2) {
} else { numUTF8bytes = 2;
onechar = DEFAULT_CHAR;
}

numUTF8bytes = 1;
numUTF8bytes = 2;
numUTF8bytes = 3;

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
mask
v[0]
v[1]

=
=
=
=

JS_InternString(cx, asciistring);
JS_NewStringCopyZ(cx, mode);
STRING_TO_JSVAL(mask);
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]
remainder
} else
if (i==2) {
utfbuf[0]
remainder
} else
if (i==3)
remainder

= utfbuf[1];
= utfbuf[2];
= 2;
= utfbuf[2];
= 1;
= 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
mask|=(js_FileHasOption(cx, mode, "read"))?
PR_RDONLY
:
mask|=(js_FileHasOption(cx, mode, "write"))?
PR_WRONLY
:
mask|=(js_FileHasOption(cx, mode, "readWrite"))? PR_RDWR
:
mask|=(js_FileHasOption(cx, mode, "append"))?
PR_APPEND
:
mask|=(js_FileHasOption(cx, mode, "create"))?
PR_CREATE_FILE :
mask|=(js_FileHasOption(cx, mode, "replace"))? PR_TRUNCATE
:

times */
0;
0;
0;
0;
0;
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_SUPPORTE
D_WITH_PIPES,
mode, file->path);
goto out;
}
/* open(SPOOLER, "| cat -v | lpr -h 2>/dev/null") -- pipe for wr
iting */
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", fi
le->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);
JSFILE_CHECK_ONE_ARG("copyTo");
JSFILE_CHECK_NATIVE("copyTo");

/* may need a second argument!*/

/* 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
jsval
char

*str;
args[1];
*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",
"readln",
"readAll",
"write",
"writeln",
"writeAll",
"list",
"mkdir",
{ "toString",
{ "toURL",
{0}

file_read, 0},
file_readln, 0},
file_readAll, 0},
file_write, 0},
file_writeln, 0},
file_writeAll, 0},
file_list, 0},
file_mkdir, 0},
file_toString, 0},
file_toURL, 0},

};
enum file_tinyid {
FILE_LENGTH
FILE_PARENT
FILE_PATH
FILE_NAME
FILE_ISDIR
FILE_ISFILE
FILE_EXISTS
FILE_CANREAD
FILE_CANWRITE
FILE_OPEN
FILE_TYPE
FILE_MODE
FILE_CREATED
FILE_MODIFIED
FILE_SIZE
FILE_RANDOMACCESS
FILE_POSITION
FILE_APPEND
FILE_REPLACE
FILE_AUTOFLUSH
FILE_ISNATIVE
};

=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=

-2,
-3,
-4,
-5,
-6,
-7,
-8,
-9,
-10,
-11,
-12,
-13,
-14,
-15,
-16,
-17,
-18,
-19,
-20,
-21,
-22,

static JSPropertySpec file_props[] = {


{"length",
FILE_LENGTH,
{"parent",
FILE_PARENT,
{"path",
FILE_PATH,
{"name",
FILE_NAME,
{"isDirectory",
FILE_ISDIR,
{"isFile",
FILE_ISFILE,
{"exists",
FILE_EXISTS,
{"canRead",
FILE_CANREAD,
{"canWrite",
FILE_CANWRITE,
{"canAppend",
FILE_APPEND,
{"canReplace",
FILE_REPLACE,
{"isOpen",
FILE_OPEN,
{"type",
FILE_TYPE,
{"mode",
FILE_MODE,
{"creationTime",
FILE_CREATED,
{"lastModified",
FILE_MODIFIED,
{"size",
FILE_SIZE,
{"hasRandomAccess", FILE_RANDOMACCESS,
{"hasAutoFlush",
FILE_AUTOFLUSH,
{"position",
FILE_POSITION,
{"isNative",
FILE_ISNATIVE,
{0}

JSPROP_ENUMERATE
JSPROP_ENUMERATE
JSPROP_ENUMERATE
JSPROP_ENUMERATE
JSPROP_ENUMERATE
JSPROP_ENUMERATE
JSPROP_ENUMERATE
JSPROP_ENUMERATE
JSPROP_ENUMERATE
JSPROP_ENUMERATE
JSPROP_ENUMERATE
JSPROP_ENUMERATE
JSPROP_ENUMERATE
JSPROP_ENUMERATE
JSPROP_ENUMERATE
JSPROP_ENUMERATE
JSPROP_ENUMERATE
JSPROP_ENUMERATE
JSPROP_ENUMERATE
JSPROP_ENUMERATE
JSPROP_ENUMERATE

| JSPROP_READONLY
| JSPROP_READONLY
| JSPROP_READONLY
| JSPROP_READONLY
| JSPROP_READONLY
| JSPROP_READONLY
| JSPROP_READONLY
| JSPROP_READONLY
| JSPROP_READONLY
| JSPROP_READONLY
| JSPROP_READONLY
| JSPROP_READONLY
| JSPROP_READONLY
| JSPROP_READONLY
| JSPROP_READONLY
| JSPROP_READONLY
| JSPROP_READONLY
| JSPROP_READONLY
| JSPROP_READONLY
},
| JSPROP_READONLY

},
},
},
},
},
},
},
},
},
},
},
},
},
},
},
},
},
},
},
},

};
/* ------------------------- 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,
break;
case UTF8:
*vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,
break;
case UCS2:
*vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,
break;
default:
JS_ReportWarning(cx, "Unsupported file type
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, ",");

asciistring));
utfstring));
unicodestring));
%d, proceeding",

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 r
eport 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"] syn
tax */
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_NUMBE
R, "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 p
lace. */
/* 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

"jsstddef.h"
<string.h>
"jstypes.h"
"jsutil.h" /* Added by JSIFY */
"jsapi.h"
"jsarray.h"
"jsatom.h"
"jscntxt.h"
"jsconfig.h"
"jsfun.h"
"jsgc.h"
"jsinterp.h"
"jslock.h"
"jsnum.h"
"jsobj.h"
"jsopcode.h"
"jsparse.h"
"jsscan.h"
"jsscope.h"
"jsscript.h"
"jsstr.h"
"jsexn.h"

enum {
CALL_ARGUMENTS
CALL_CALLEE
ARGS_LENGTH
ARGS_CALLEE
FUN_ARITY
FUN_NAME
FUN_CALL
FUN_CALLER
};

=
=
=
=
=
=
=
=

-1,
-2,
-3,
-4,
-5,
-6,
-7,
-8

#undef TEST_BIT
#undef SET_BIT
#define TEST_BIT(tinyid, bitset)
#define SET_BIT(tinyid, bitset)

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

predefined arguments local variable */


reference to active function's object */
number of actual args, arity if inactive */
reference from arguments to active funobj */
number of formal parameters; desired argc */
function name, "" if anonymous */
function's top Call object in this context */
Function.prototype.caller, backward compat */

((bitset) & JS_BIT(-(tinyid) - 1))


((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
* before fp goes away.
*/
rt = cx->runtime;
ok &= js_GetProperty(cx,
ok &= js_SetProperty(cx,
ok &= js_GetProperty(cx,
ok &= js_SetProperty(cx,

properties so we snapshot fp->fun and fp->argc

argsobj,
argsobj,
argsobj,
argsobj,

(jsid)rt->atomState.calleeAtom,
(jsid)rt->atomState.calleeAtom,
(jsid)rt->atomState.lengthAtom,
(jsid)rt->atomState.lengthAtom,

&rval);
&rval);
&rval);
&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,
{0,0,0,0,0}

ARGS_CALLEE,

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
case
case
case
if

(slot) {
CALL_ARGUMENTS:
CALL_CALLEE:
FUN_CALL:
(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,
{js_arity_str,
FUN_ARITY,
{js_length_str,
ARGS_LENGTH,
{js_name_str,
FUN_NAME,
{"__call__",
FUN_CALL,
{js_caller_str,
FUN_CALLER,
{0,0,0,0,0}
};

= {
FUNCTION_PROP_ATTRS,0,0},
FUNCTION_PROP_ATTRS,0,0},
FUNCTION_PROP_ATTRS,0,0},
FUNCTION_PROP_ATTRS,0,0},
FUNCTION_PROP_ATTRS,0,0},
FUNCTION_PROP_ATTRS,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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

"jstypes.h"
"jsarena.h" /* Added by JSIFY */
"jsutil.h" /* Added by JSIFY */
"jshash.h" /* Added by JSIFY */
"jsapi.h"
"jsatom.h"
"jscntxt.h"
"jsconfig.h"
"jsgc.h"
"jsinterp.h"
"jslock.h"
"jsnum.h"
"jsobj.h"
"jsscope.h"
"jsscript.h"
"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] =
gc_finalizers[GCX_STRING] =
#ifdef DEBUG
gc_finalizers[GCX_DOUBLE] =
#endif
}

{
(GCFinalizeOp)js_FinalizeObject;
(GCFinalizeOp)js_FinalizeString;
(GCFinalizeOp)js_FinalizeDouble;

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",
fprintf(fp, "
alloc attempts: %lu\n",
fprintf(fp, "
GC freelist length: %lu\n",
fprintf(fp, " recycles through GC freelist: %lu\n",
fprintf(fp, "alloc retries after running GC: %lu\n",
fprintf(fp, "
allocation failures: %lu\n",
fprintf(fp, "
valid lock calls: %lu\n",
fprintf(fp, "
valid unlock calls: %lu\n",
fprintf(fp, " locks that hit stuck counts: %lu\n",
fprintf(fp, " unlocks that saw stuck counts: %lu\n",
fprintf(fp, "
mark recursion depth: %lu\n",
fprintf(fp, " maximum mark recursion depth: %lu\n",
fprintf(fp, "
maximum GC nesting level: %lu\n",
fprintf(fp, " potentially useful GC calls: %lu\n",
fprintf(fp, "
useless GC calls: %lu\n",
fprintf(fp, "
thing arenas freed so far: %lu\n",
fprintf(fp, " extra stack segments scanned: %lu\n",
fprintf(fp, " stack segment slots scanned: %lu\n",
#ifdef JS_ARENAMETER
JS_DumpArenaStats(fp);
#endif
}
#endif

rt->gcBytes);
rt->gcStats.alloc);
rt->gcStats.freelen);
rt->gcStats.recycle);
rt->gcStats.retry);
rt->gcStats.fail);
rt->gcStats.lock);
rt->gcStats.unlock);
rt->gcStats.stuck);
rt->gcStats.unstuck);
rt->gcStats.depth);
rt->gcStats.maxdepth);
rt->gcStats.maxlevel);
rt->gcStats.poke);
rt->gcStats.nopoke);
rt->gcStats.afree);
rt->gcStats.stackseg);
rt->gcStats.segslots);

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

gc_lock_get_count(he)
gc_lock_set_count(he,n)
gc_lock_increment(he)
gc_lock_decrement(he)

((jsrefcount)(he)->value)
((jsrefcount)((he)->value = (void *)(n)))
gc_lock_set_count(he, gc_lock_get_count(he)+1)
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;
return num >> 2;

/* help lame MSVC1.5 on Win16 */

}
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
#define GCX_STRING
#define GCX_DOUBLE
#define GCX_EXTERNAL_STRING
#define GCX_NTYPES_LOG2
#define GCX_NTYPES
/* GC flag definitions,
#define GCF_TYPEMASK
#define GCF_MARK
#define GCF_FINAL
#define GCF_LOCKSHIFT
#define GCF_LOCKMASK
#define GCF_LOCK

0
/* JSObject */
1
/* JSString */
2
/* jsdouble */
3
/* JSString w/ external chars */
3
/* type index bits */
JS_BIT(GCX_NTYPES_LOG2)

must fit in 8 bits (type index goes in the low bits). */


JS_BITMASK(GCX_NTYPES_LOG2)
JS_BIT(GCX_NTYPES_LOG2)
JS_BIT(GCX_NTYPES_LOG2 + 1)
(GCX_NTYPES_LOG2 + 2) /* lock bit shift and mask */
(JS_BITMASK(8 - GCF_LOCKSHIFT) << GCF_LOCKSHIFT)
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;
/*
uint32 freelen;
/*
uint32 recycle;
/*
uint32 retry;
/*
uint32 fail;
/*
uint32 finalfail; /*
uint32 lock;
/*
uint32 unlock;
/*
uint32 stuck;
/*
uint32 unstuck;
/*
uint32 depth;
/*
uint32 maxdepth; /*
uint32 maxlevel; /*
uint32 poke;
/*
uint32 nopoke;
/*
uint32 afree;
/*
uint32 stackseg; /*
uint32 segslots; /*

number of allocation attempts */


gcFreeList length */
number of things recycled through gcFreeList */
allocation attempt retries after running the GC */
allocation failures */
finalizer calls allocator failures */
valid lock calls */
valid unlock calls */
stuck reference counts seen by lock calls */
unlock calls that saw a stuck lock count */
mark recursion depth */
maximum mark recursion depth */
maximum GC nesting (indirect recursion) level */
number of potentially useful GC calls */
useless GC calls where js_PokeGC was not set */
thing arenas freed so far */
total extraordinary stack segments scanned */
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,
fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,

"\nHash table statistics:\n");


"
number of lookups: %u\n",
"
number of entries: %u\n",
"
number of grows: %u\n",
"
number of shrinks: %u\n",
" mean steps per hash: %g\n",

fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,

"mean hash chain length:


"
standard deviation:
" max hash chain length:
"
max hash chain:

ht->nlookups);
ht->nentries);
ht->ngrows);
ht->nshrinks);
(double)ht->nsteps
/ ht->nlookups);
%g\n", mean);
%g\n", sigma);
%u\n", maxChainLen);
[%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
#define
#define
#define
#define

bits in JSHashEnumerator's return value */


HT_ENUMERATE_NEXT
0
/* continue enumerating entries */
HT_ENUMERATE_STOP
1
/* stop enumerating entries */
HT_ENUMERATE_REMOVE
2
/* remove and free the current entry */
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
#define HT_FREE_ENTRY

0
1

/* just free the entry's value */


/* free value and entire entry */

struct JSHashEntry {
JSHashEntry
JSHashNumber
const void
void
};

*next;
keyHash;
*key;
*value;

/*
/*
/*
/*

hash chain linkage */


key hash function result */
ptr to opaque key */
ptr to opaque value */

**buckets;
nentries;
shift;
keyHash;
keyCompare;
valueCompare;
*allocOps;
*allocPriv;

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

vector of hash buckets */


number of entries in table */
multiplicative hash shift */
key hash function */
key comparison function */
value comparison function */
allocation operations */
allocation private data */

nlookups;
nsteps;
ngrows;
nshrinks;

/*
/*
/*
/*

total number of lookups */


number of hash chains traversed */
number of table expansions */
number of table contractions */

struct JSHashTable {
JSHashEntry
uint32
uint32
JSHashFunction
JSHashComparator
JSHashComparator
JSHashAllocOps
void
#ifdef HASHMETER
uint32
uint32
uint32
uint32
#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
-outdir

Reverse direction of transformation, i.e. JS ==> NSPR2


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 dec
lare

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
#define JSSLOT_ITR_STATE
#else
#define JSSLOT_ITER_STATE
#define JSSLOT_ITR_STATE
#endif

(JSSLOT_FREE(&prop_iterator_class))
(JSSLOT_FREE(&prop_iterator_class))
(JSSLOT_START)
(JSSLOT_START)

//#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! othe
rwise we'll crash! (ajf 12/4/2002)
js_RemoveRoot(cx->runtime, &obj->slots[J
SSLOT_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
if (fp->constructing)
fp->rval = OBJECT_TO_JSVAL(thisp);
} else {
/*
* ECMA requires "the global object", but
* top-level objects (windows, frames, or
* object model), we prefer fun's parent.
* 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);

is the new object. */

in the presence of multiple


certain layers in the client
An example that causes this

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;
frame.pc = NULL;
frame.sharpDepth = 0;
frame.sharpArray = NULL;
frame.overrides = 0;
frame.special = 0;
frame.dormantNext = NULL;

/* set below for real, after cx->fp is set */

/* 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_JSVA
L(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)
#define SIGNED_SHIFT_OP(OP)

\
\
\
\
\
\
\
\
\
\

INTEGER_OP(OP, (void) 0;)


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,
#ifdef HPUX
/*
* Negation of a
* zero on HPUX.
* twiddling.
*/
JSDOUBLE_HI32(d)
#else
d = -d;
#endif
STORE_NUMBER(cx,
break;

-1, d);
zero doesn't produce a negative
Perform the operation by bit
^= JSDOUBLE_HI32_SIGNBIT;

-1, d);

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),
case JSOP_DECARG:
FAST_INCREMENT_OP(GET_ARGNO(pc),
case JSOP_ARGINC:
FAST_INCREMENT_OP(GET_ARGNO(pc),
case JSOP_ARGDEC:
FAST_INCREMENT_OP(GET_ARGNO(pc),
case JSOP_INCVAR:
FAST_INCREMENT_OP(GET_VARNO(pc),
case JSOP_DECVAR:
FAST_INCREMENT_OP(GET_VARNO(pc),
case JSOP_VARINC:
FAST_INCREMENT_OP(GET_VARNO(pc),
case JSOP_VARDEC:
FAST_INCREMENT_OP(GET_VARNO(pc),

nargs, argv, rval, +=, MAX);


nargs, argv, rval, -=, MIN);
nargs, argv, rtmp, +=, MAX);
nargs, argv, rtmp, -=, MIN);
nvars, vars, rval, +=, MAX);
nvars, vars, rval, -=, MIN);
nvars, vars, rtmp, +=, MAX);
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;
/*
id
ok
if

Lookup id in order to check for redeclaration problems. */


= (jsid)atom;
= js_CheckRedeclaration(cx, obj, id, attrs, &defined);
(!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
* clone's parent. The original function object is
* of the clone. Do this only if re-parenting; the
* have seen the right parent already and created a
* 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;
}
}

chain as the
the prototype
compiler may
sufficiently

/*
* 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
*/
struct JSStackFrame {
JSObject
*callobj;
JSObject
*argsobj;
JSObject
*varobj;
JSScript
*script;
JSFunction
*fun;
JSObject
*thisp;
uintN
argc;
jsval
*argv;
jsval
rval;
uintN
nvars;
jsval
*vars;
JSStackFrame
*down;
void
*annotation;
JSObject
*scopeChain;
jsbytecode
*pc;
jsval
*sp;
jsval
*spbase;
uintN
sharpDepth;
JSObject
*sharpArray;
JSPackedBool
constructing;
uint8
overrides;
uint8
special;

C stack.
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*

lazily created Call object */


lazily created arguments object */
variables object, where vars go */
script being interpreted */
function being called or null */
"this" pointer if in method */
actual argument count */
base of argument stack slots */
function return value */
local variable count */
base of variable stack slots */
previous frame */
used by Java security */
scope chain */
program counter */
stack pointer */
operand stack base */
array/object initializer depth */
scope for #n= initializer vars */
true if called via new operator */
bit-set of overridden Call properties */
special frame type flags, see below */

JSPackedBool
JSStackFrame

reserved;
*dormantNext;

/* reserved for future use */


/* next dormant frame chain */

};
typedef struct JSInlineFrame {
JSStackFrame
frame;
void
*mark;
void
*hookData;
} JSInlineFrame;

/* base struct */
/* mark before inline frame */
/* debugger call hook data */

/* 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
*/
#define PROPERTY_CACHE_LOG2
#define PROPERTY_CACHE_SIZE
#define PROPERTY_CACHE_MASK

get/set property opcodes.


10
JS_BIT(PROPERTY_CACHE_LOG2)
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)
#define PCE_STORE(cache, pce, entry)

JS_ATOMIC_DWORD_LOAD(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)
#define PCE_STORE(cache, pce, entry)

((entry) = *(pce))
(*(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
#define
#define
#define
#define

fd_acos acos
fd_asin asin
fd_atan atan
fd_atan2 atan2
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
#define
#define
#define
#define
#define
#define
#define
#define
#define

fd_cos cos
fd_exp exp
fd_fabs fabs
fd_floor floor
fd_fmod fmod
fd_log log
fd_pow pow
fd_sin sin
fd_sqrt sqrt
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
#define
#define
#define
#define

fd_acos acos
fd_asin asin
fd_atan atan
fd_cos cos
fd_sin sin

#define
#define
#define
#define
#define
#define
#define
#define

fd_tan tan
fd_exp exp
fd_log log
fd_sqrt sqrt
fd_ceil ceil
fd_fabs fabs
fd_floor floor
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
#define
#define
#define
#define
#define
#define
#define
#define
#define
extern
extern
extern
extern
extern
extern
extern

fd_acos acos
fd_asin asin
fd_atan atan
fd_exp exp
fd_log log
fd_log10 log10
fd_sqrt sqrt
fd_fabs fabs
fd_floor floor
fd_fmod fmod
double
double
double
double
double
double
double

fd_cos __P((double));
fd_sin __P((double));
fd_tan __P((double));
fd_atan2 __P((double, double));
fd_pow __P((double, double));
fd_ceil __P((double));
fd_copysign __P((double, double));

#elif defined SOLARIS


#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
extern
extern
extern
extern
extern
extern

fd_atan atan
fd_cos cos
fd_sin sin
fd_tan tan
fd_exp exp
fd_sqrt sqrt
fd_ceil ceil
fd_fabs fabs
fd_floor floor
fd_fmod fmod
double
double
double
double
double
double

fd_acos __P((double));
fd_asin __P((double));
fd_log __P((double));
fd_atan2 __P((double, double));
fd_pow __P((double, double));
fd_copysign __P((double, double));

#elif defined HPUX


#define
#define
#define
#define

fd_cos cos
fd_sin sin
fd_exp exp
fd_sqrt sqrt

#define fd_fabs fabs


#define fd_floor floor
#define fd_fmod fmod
extern
extern
extern
extern
extern
extern
extern
extern
extern

double
double
double
double
double
double
double
double
double

fd_ceil __P((double));
fd_acos __P((double));
fd_log __P((double));
fd_atan2 __P((double, double));
fd_tan __P((double));
fd_pow __P((double, double));
fd_asin __P((double));
fd_atan __P((double));
fd_copysign __P((double, double));

#elif defined(linux)
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
extern
extern
extern
extern
extern

fd_atan atan
fd_atan2 atan2
fd_ceil ceil
fd_cos cos
fd_fabs fabs
fd_floor floor
fd_fmod fmod
fd_sin sin
fd_sqrt sqrt
fd_tan tan
fd_copysign copysign
double
double
double
double
double

fd_asin __P((double));
fd_acos __P((double));
fd_exp __P((double));
fd_log __P((double));
fd_pow __P((double, double));

#elif defined(OSF1)
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
extern
extern
extern
extern
extern

fd_acos acos
fd_asin asin
fd_atan atan
fd_copysign copysign
fd_cos cos
fd_exp exp
fd_fabs fabs
fd_fmod fmod
fd_sin sin
fd_sqrt sqrt
fd_tan tan
double
double
double
double
double

fd_atan2 __P((double, double));


fd_ceil __P((double));
fd_floor __P((double));
fd_log __P((double));
fd_pow __P((double, double));

#elif defined(AIX)
#define
#define
#define
#define

fd_acos acos
fd_asin asin
fd_atan2 atan2
fd_copysign copysign

#define
#define
#define
#define
#define
#define
#define
#define
extern
extern
extern
extern

fd_cos cos
fd_exp exp
fd_fabs fabs
fd_floor floor
fd_fmod fmod
fd_log log
fd_sin sin
fd_sqrt sqrt
double
double
double
double

fd_atan __P((double));
fd_ceil __P((double));
fd_pow __P((double,double));
fd_tan __P((double));

#elif defined(XP_OS2_VACPP)
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define

fd_acos acos
fd_asin asin
fd_atan2 atan2
fd_cos cos
fd_exp exp
fd_fabs fabs
fd_floor floor
fd_fmod fmod
fd_log log
fd_sin sin
fd_sqrt sqrt
fd_atan atan
fd_ceil ceil
fd_pow pow
fd_tan tan

/* OS2 lacks copysign */


extern double fd_copysign __P((double, double));
#else /* other platform.. generic paranoid slow fdlibm */
extern
extern
extern
extern
extern
extern

double
double
double
double
double
double

fd_acos __P((double));
fd_asin __P((double));
fd_atan __P((double));
fd_cos __P((double));
fd_sin __P((double));
fd_tan __P((double));

extern double fd_exp __P((double));


extern double fd_log __P((double));
extern double fd_sqrt __P((double));
extern
extern
extern
extern

double
double
double
double

fd_ceil __P((double));
fd_fabs __P((double));
fd_floor __P((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
static
static
static

PRLock
uint32
uint32
uint32

**global_locks;
global_lock_count = 1;
global_locks_log2 = 0;
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
while ((ownercx = scope->ownercx) != NULL) {
/*
* Avoid selflock if ownercx is dead, or is not running
* has the same thread as cx. Set scope->ownercx to cx
* matching JS_UNLOCK_SCOPE or JS_UNLOCK_OBJ macro call
* fast path around the corresponding js_UnlockScope or
* function call.
*
* If scope->u.link is non-null, scope has already been

lock. */
a request, or
so that the
will take the
js_UnlockObj
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);
return;

/* unbalanced unlock */

}
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
#include
#include
#include
#include

"jstypes.h"
"pratom.h"
"prlock.h"
"prcvar.h"
"jshash.h" /* Added by JSIFY */

#include "jsprvtd.h"
#include "jspubtd.h"

/* for JSScope, etc. */


/* 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
JSFatLock
} JSThinLock;

owner;
*fat;

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

CurrentThreadId()
JS_CurrentThreadId()
JS_NEW_LOCK()
JS_DESTROY_LOCK(l)
JS_ACQUIRE_LOCK(l)
JS_RELEASE_LOCK(l)
JS_LOCK0(P,M)
JS_UNLOCK0(P,M)

(jsword)PR_GetCurrentThread()
js_CurrentThreadId()
PR_NewLock()
PR_DestroyLock(l)
PR_Lock(l)
PR_Unlock(l)
js_Lock(P,M)
js_Unlock(P,M)

#define
#define
#define
#define
#define
#define

JS_NEW_CONDVAR(l)
JS_DESTROY_CONDVAR(cv)
JS_WAIT_CONDVAR(cv,to)
JS_NO_TIMEOUT
JS_NOTIFY_CONDVAR(cv)
JS_NOTIFY_ALL_CONDVAR(cv)

PR_NewCondVar(l)
PR_DestroyCondVar(cv)
PR_WaitCondVar(cv,to)
PR_INTERVAL_NO_TIMEOUT
PR_NotifyCondVar(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)
#define JS_UNLOCK_RUNTIME(rt)

js_LockRuntime(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
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern

jsword js_CurrentThreadId();
void js_LockRuntime(JSRuntime *rt);
void js_UnlockRuntime(JSRuntime *rt);
void js_LockObj(JSContext *cx, JSObject *obj);
void js_UnlockObj(JSContext *cx, JSObject *obj);
void js_LockScope(JSContext *cx, JSScope *scope);
void js_UnlockScope(JSContext *cx, JSScope *scope);
int js_SetupLocks(int,int);
void js_CleanupLocks();
void js_InitContextForLocking(JSContext *);
void js_TransferScopeLock(JSContext *, JSScope *, JSScope *);
jsval js_GetSlotThreadSafe(JSContext *, JSObject *, uint32);
void js_SetSlotThreadSafe(JSContext *, JSObject *, uint32, jsval);
void js_InitLock(JSThinLock *);
void js_FinishLock(JSThinLock *);

#ifdef DEBUG
#define JS_IS_RUNTIME_LOCKED(rt)
#define JS_IS_OBJ_LOCKED(obj)
#define JS_IS_SCOPE_LOCKED(scope)

js_IsRuntimeLocked(rt)
js_IsObjLocked(obj)
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)
#define JS_IS_OBJ_LOCKED(obj)
#define JS_IS_SCOPE_LOCKED(scope)
#endif /* DEBUG */

0
1
1

#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)
#define JS_ATOMIC_DECREMENT(p)

(++*(p))
(--*(p))

#define
#define
#define
#define
#define
#define
#define

JS_CurrentThreadId() 0
JS_NEW_LOCK()
JS_DESTROY_LOCK(l)
JS_ACQUIRE_LOCK(l)
JS_RELEASE_LOCK(l)
JS_LOCK0(P,M)
JS_UNLOCK0(P,M)

NULL
((void)0)
((void)0)
((void)0)
((void)0)
((void)0)

#define
#define
#define
#define
#define

JS_NEW_CONDVAR(l)
JS_DESTROY_CONDVAR(cv)
JS_WAIT_CONDVAR(cv,to)
JS_NOTIFY_CONDVAR(cv)
JS_NOTIFY_ALL_CONDVAR(cv)

NULL
((void)0)
((void)0)
((void)0)
((void)0)

#define
#define
#define
#define
#define

JS_LOCK_RUNTIME(rt)
JS_UNLOCK_RUNTIME(rt)
JS_LOCK_OBJ(cx,obj)
JS_UNLOCK_OBJ(cx,obj)
JS_LOCK_OBJ_VOID(cx,obj,e)

((void)0)
((void)0)
((void)0)
((void)0)
(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
#define
#define
#define

JS_IS_RUNTIME_LOCKED(rt)
JS_IS_OBJ_LOCKED(obj)
JS_IS_SCOPE_LOCKED(scope)
JS_LOCK_VOID(cx, e)

1
1
1
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
#define
#define
#define
#define
#define

\
\
\
\
\

JS_LOCK_GC(rt)
JS_UNLOCK_GC(rt)
JS_LOCK_GC_VOID(rt,e)
JS_AWAIT_GC_DONE(rt)
JS_NOTIFY_GC_DONE(rt)
JS_AWAIT_REQUEST_DONE(rt)

JS_ACQUIRE_LOCK((rt)->gcLock)
JS_RELEASE_LOCK((rt)->gcLock)
(JS_LOCK_GC(rt), (e), JS_UNLOCK_GC(rt))
JS_WAIT_CONDVAR((rt)->gcDone, JS_NO_TIMEOUT)
JS_NOTIFY_ALL_CONDVAR((rt)->gcDone)
JS_WAIT_CONDVAR((rt)->requestDone,
\
JS_NO_TIMEOUT)
#define JS_NOTIFY_REQUEST_DONE(rt) JS_NOTIFY_CONDVAR((rt)->requestDone)
#define JS_LOCK(P,CX)
#define JS_UNLOCK(P,CX)
#ifndef
#define
#endif
#ifndef
#define
#endif

JS_LOCK0(P,(CX)->thread)
JS_UNLOCK0(P,(CX)->thread)

SET_OBJ_INFO
SET_OBJ_INFO(obj,f,l)

((void)0)

SET_SCOPE_INFO
SET_SCOPE_INFO(scope,f,l)

((void)0)

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


4 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)
b.lo = 1 / b.lo;

/* user wants to divide by zero! */


/* 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.lo
n2 =
n1 =
n0 =

= (b.hi << lsh) | (b.lo >> rsh);


= b.lo << lsh;
n1 >> rsh;
(n1 << lsh) | (n0 >> rsh);
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
#define JSLL_MININT
#define JSLL_ZERO

JSLL_MaxInt()
JSLL_MinInt()
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)
#define JSLL_ADD(r, a, b)
#define JSLL_SUB(r, a, b)

((r) = -(a))
((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
#define
#define
#define

JSLL_I2L(l, i)
JSLL_UI2L(l, ui)
JSLL_F2L(l, f)
JSLL_D2L(l, d)

((l) =
((l)
((l) =
((l) =

(JSInt64)(i))
= (JSInt64)(ui))
(JSInt64)(f))
(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
#define
#define
#define

JSLL_IS_ZERO(a)
JSLL_EQ(a, b)
JSLL_NE(a, b)
JSLL_GE_ZERO(a)

#ifdef DEBUG
#define JSLL_CMP(a,
, b))
#define JSLL_CMP(a,
, b))
#else
#define JSLL_CMP(a,
#define JSLL_CMP(a,
#endif

(((a).hi
(((a).hi
(((a).hi
(((a).hi

==
==
!=
>>

0) && ((a).lo == 0))


(b).hi) && ((a).lo == (b).lo))
(b).hi) || ((a).lo != (b).lo))
31) == 0)

op, b)

(JS_ASSERT((#op)[1] != '='), JSLL_REAL_CMP(a, op

op, b)

(JS_ASSERT((#op)[1] != '='), JSLL_REAL_CMP(a, op

op, b)
op, b)

JSLL_REAL_CMP(a, op, b)
JSLL_REAL_CMP(a, op, b)

#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)
#define JSLL_OR(r, a, b)
#define JSLL_XOR(r, a, b)
#define JSLL_OR2(r, a)
#define JSLL_NOT(r, a)
#define JSLL_NEG(r, a)

((r).lo
(r).hi
((r).lo
(r).hi
((r).lo
(r).hi
((r).lo
(r).hi
((r).lo
(r).hi

=
=
=
=
=
=
=
=
=
=

(a).lo &
(a).hi &
(a).lo |
(a).hi |
(a).lo ^
(a).hi ^
(r).lo |
(r).hi |
~(a).lo,
~(a).hi)

\
\
\
\

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

(b).lo,
(b).hi)
(b).lo,
(b).hi)
(b).lo,
(b).hi)
(a).lo,
(a).hi)
\

_a = a; _b = b; \
JSLL_MUL32(r, _a.lo, _b.lo); \
(r).hi += _a.hi * _b.lo + _a.lo * _b.hi; \
}
#define jslo16(a)
#define jshi16(a)

((a) & JS_BITMASK(16))


((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)
#define JSLL_L2UI(ui, l)
#define JSLL_L2F(f, l)
}

((i) = (l).lo)
((ui) = (l).lo)
{ 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)
i; }
#define JSLL_UI2L(l, ui)
#define JSLL_F2L(l, f)

{ JSInt32 _i = (i) >> 31; (l).lo = (i); (l).hi = _


((l).lo = (ui), (l).hi = 0)
{ 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
#include
#include
#include
#ifndef
#define
#endif
#ifndef
#define
#endif
#ifndef
#define
#endif
#ifndef
#define
#endif
#ifndef
#define
#endif
#ifndef
#define
#endif
#ifndef
#define
#endif
#ifndef
#define
#endif

"jslock.h"
"jsmath.h"
"jsnum.h"
"jsobj.h"
M_E
M_E

2.7182818284590452354

M_LOG2E
M_LOG2E

1.4426950408889634074

M_LOG10E
M_LOG10E

0.43429448190325182765

M_LN2
M_LN2

0.69314718055994530942

M_LN10
M_LN10

2.30258509299404568402

M_PI
M_PI

3.14159265358979323846

M_SQRT2
M_SQRT2

1.41421356237309504880

M_SQRT1_2
M_SQRT1_2

0.70710678118654752440

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 Reser
ved.
*/
#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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

<math.h>
<stdlib.h>
<string.h>
"jstypes.h"
"jsutil.h" /* Added by JSIFY */
"jsapi.h"
"jsatom.h"
"jscntxt.h"
"jsconfig.h"
"jsdtoa.h"
"jsgc.h"
"jsinterp.h"
"jsnum.h"
"jsobj.h"
"jsopcode.h"
"jsprf.h"
"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 *rva
l)
{
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
const
const
const
const
const

char
char
char
char
char
char

js_Infinity_str[]
js_NaN_str[]
js_isNaN_str[]
js_isFinite_str[]
js_parseFloat_str[]
js_parseInt_str[]

=
=
=
=
=
=

"Infinity";
"NaN";
"isNaN";
"isFinite";
"parseFloat";
"parseInt";

static JSFunctionSpec number_functions[] = {


{"isNaN",
num_isNaN,
{"isFinite",
num_isFinite,
{"parseFloat",
num_parseFloat,
{"parseInt",
num_parseInt,
{0,0,0,0,0}
};
static JSClass number_class = {
"Number",

1,0,0},
1,0,0},
1,0,0},
2,0,0},

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, JSDTo
StrMode zeroArgMode,
JSDToStrMode oneArgMode, jsint precisionMin, jsint precisionMax, jsint pr
ecisionOffset)
{
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_PRECISI
ON_RANGE, numStr);
return JS_FALSE;
}
}
numStr = JS_dtostr(buf, sizeof buf, oneArgMode, (jsint)precision + precision
Offset, 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 permitte
d by ECMA. */
return num_to(cx, obj, argc, argv, rval, DTOSTR_FIXED, DTOSTR_FIXED, -20, MA

X_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 permitte
d 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 *rv
al)
{
/* We allow a larger range of precision than ECMA requires; this is permitte
d 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,
#endif
{js_toString_str,
num_toString,
{js_toLocaleString_str, num_toLocaleString,
{js_valueOf_str,
num_valueOf,
#if JS_HAS_NUMBER_FORMATS
{"toFixed",
num_toFixed,
{"toExponential",
num_toExponential,
{"toPrecision",
num_toPrecision,
#endif
{0,0,0,0,0}
};

0,0,0},
0,0,0},
0,0,0},
0,0,0},
1,0,0},
1,0,0},
1,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,
{0,0,0,{0,0,0}}

"MIN_VALUE",

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

/* Round to nearest */

}
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->jsPositi
veInfinity);
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;
uintN digit;
uintN digitMask;
const jschar *digits;
const jschar *end;
};

/*
/*
/*
/*
/*

Base of
Current
Mask to
Pointer
Pointer

number; must be a power of 2 */


digit value in radix given by base */
extract the next bit from digit */
to the remaining digits */
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, j
sdouble *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)
#define JSDOUBLE_LO32(x)
#else
#define JSDOUBLE_HI32(x)
#define JSDOUBLE_LO32(x)
#endif
#define JSDOUBLE_HI32_SIGNBIT
#define JSDOUBLE_HI32_EXPMASK
#define JSDOUBLE_HI32_MANTMASK

(((uint32 *)&(x))[1])
(((uint32 *)&(x))[0])
(((uint32 *)&(x))[0])
(((uint32 *)&(x))[1])
0x80000000
0x7ff00000
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 corre
ct
* 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 th
e
* 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 pow
er 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

/* 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 hasOwnP
roperty. */
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_NewObjectMap,
with_LookupProperty,
with_GetProperty,
with_GetAttributes,
with_DeleteProperty,
with_Enumerate,
with_ThisObject,
NULL,
NULL,
js_SetProtoOrParent,
js_Mark,
0,
};

js_WithObjectOps = {
js_DestroyObjectMap,
js_DefineProperty,
with_SetProperty,
with_SetAttributes,
with_DefaultValue,
with_CheckAccess,
NATIVE_DROP_PROPERTY,
NULL,
NULL,
js_SetProtoOrParent,
js_Clear,
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;
JSObjectOps *ops;
uint32
nslots;
uint32
freeslot;
};

/*
/*
/*
/*

count of all referencing objects */


high level object operation vtable */
length of obj->slots vector */
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
#define
#define
#define
#define

JSSLOT_PROTO
JSSLOT_PARENT
JSSLOT_CLASS
JSSLOT_PRIVATE
JSSLOT_START

0
1
2
3
3

#define JSSLOT_FREE(clasp) (((clasp)->flags & JSCLASS_HAS_PRIVATE)


? JSSLOT_PRIVATE + 1
: JSSLOT_START)
#define JS_INITIAL_NSLOTS

\
\

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

JS_FRIEND_DATA(JSObjectOps) js_ObjectOps;
JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps;
JSClass js_ObjectClass;
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
#include
#include
#include
#include
#include
#include
char
char
char
char
char
char
char
char
char
char
char
char
char

"jsfun.h"
"jslock.h"
"jsobj.h"
"jsopcode.h"
"jsscope.h"
"jsscript.h"
"jsstr.h"

js_const_str[]
js_var_str[]
js_function_str[]
js_in_str[]
js_instanceof_str[]
js_new_str[]
js_delete_str[]
js_typeof_str[]
js_void_str[]
js_null_str[]
js_this_str[]
js_false_str[]
js_true_str[]

char *js_incop_str[]

=
=
=
=
=
=
=
=
=
=
=
=
=

"const";
"var";
"function";
"in";
"instanceof";
"new";
"delete";
"typeof";
"void";
"null";
"this";
"false";
"true";

= {"++", "--"};

/* 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;
JSArenaPool
pool;
uintN
indent;
JSBool
pretty;
JSScript
*script;
JSScope
*scope;
};

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

base class state */


string allocation pool */
indentation in spaces */
pretty-print: indent, use newlines */
script being printed */
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
Sprinter
ptrdiff_t
jsbytecode
uintN
JSPrinter
} SprintStack;

SprintStack {
sprinter;
*offsets;
*opcodes;
top;
*printer;

/*
/*
/*
/*
/*

sprinter for postfix to infix buffering */


stack of postfix string offsets */
parallel stack of JS opcodes */
top of stack index */
permanent output goes here */

/* 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)
#define POP_STR()
#define LOCAL_ASSERT(expr)

if (!Decompile(ss, pc, nb)) return JS_FALSE


OFF2STR(&ss->sprinter, PopOff(ss, op))
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
#define JOF_JUMP
#define JOF_CONST

0
1
2

/* single bytecode, no immediates */


/* signed 16-bit jump offset immediate */
/* unsigned 16-bit constant pool index */

#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define

JOF_UINT16
JOF_TABLESWITCH
JOF_LOOKUPSWITCH
JOF_QARG
JOF_QVAR
JOF_DEFLOCALVAR
JOF_TYPEMASK
JOF_NAME
JOF_PROP
JOF_ELEM
JOF_MODEMASK
JOF_SET
JOF_DEL
JOF_DEC
JOF_INC
JOF_INCDEC
JOF_POST
JOF_IMPORT

3
4
5
6
7
8
0x000f
0x0010
0x0020
0x0030
0x0030
0x0040
0x0080
0x0100
0x0200
0x0300
0x0400
0x0800

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

unsigned 16-bit immediate operand */


table switch */
lookup switch */
quickened get/set function argument ops */
quickened get/set local variable ops */
define local var with initial value */
mask for above immediate types */
name operation */
obj.prop operation */
obj[index] operation */
mask for above addressing modes */
set (i.e., assignment) operation */
delete operation */
decrement (--, not ++) opcode */
increment (++, not --) opcode */
increment or decrement opcode */
postorder increment or decrement */
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
#define
#define
#define
#define

ATOM_INDEX_LEN
2
ATOM_INDEX_HI(index)
((jsbytecode)((index) >> 8))
ATOM_INDEX_LO(index)
((jsbytecode)(index))
GET_ATOM_INDEX(pc)
((jsatomid)(((pc)[1] << 8) | (pc)[2]))
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
#define
#define
#define

ARGC_HI(argc)
ARGC_LO(argc)
GET_ARGC(pc)
ARGC_LIMIT

((jsbytecode)((argc) >> 8))


((jsbytecode)(argc))
((uintN)(((pc)[1] << 8) | (pc)[2]))
((uint32)1 << 16)

/* Synonyms for quick JOF_QARG


#define GET_ARGNO(pc)
#define SET_ARGNO(pc,argno)
#define ARGNO_LEN
#define GET_VARNO(pc)
#define SET_VARNO(pc,varno)
#define VARNO_LEN
struct JSCodeSpec {
const char
const char
int8
int8

*name;
*token;
length;
nuses;

and JOF_QVAR bytecodes. */


GET_ARGC(pc)
SET_JUMP_OFFSET(pc,argno)
JUMP_OFFSET_LEN
GET_ARGC(pc)
SET_JUMP_OFFSET(pc,varno)
JUMP_OFFSET_LEN
/*
/*
/*
/*

JS bytecode name */
JS source literal or null */
length including opcode byte */
arity, -1 if variadic */

\
\

int8
uint8
uint32

ndefs;
prec;
format;

/* number of stack results */


/* operator precedence */
/* immediate operand format */

};
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern

char
char
char
char
char
char
char
char
char
char
char
char
char
JSCodeSpec
uintN
jschar

js_const_str[];
js_var_str[];
js_function_str[];
js_in_str[];
js_instanceof_str[];
js_new_str[];
js_delete_str[];
js_typeof_str[];
js_void_str[];
js_null_str[];
js_this_str[];
js_false_str[];
js_true_str[];
js_CodeSpec[];
js_NumCodeSpecs;
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
#define JSDVG_SEARCH_STACK

0
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:
01234567890123456789012345678901234567890123456789012345678901234567890123456789
012345678901234567890123456789012345
*/
/* legend: op

val name

image

/* Longstanding JavaScript bytecodes. */


OPDEF(JSOP_NOP,
0, "nop",
NULL,
OPDEF(JSOP_PUSH,
1, "push",
NULL,
OPDEF(JSOP_POPV,
2, "popv",
NULL,
OPDEF(JSOP_ENTERWITH, 3, "enterwith", NULL,
OPDEF(JSOP_LEAVEWITH, 4, "leavewith", NULL,
OPDEF(JSOP_RETURN,
5, "return",
NULL,
OPDEF(JSOP_GOTO,
6, "goto",
NULL,
OPDEF(JSOP_IFEQ,
7, "ifeq",
NULL,
OPDEF(JSOP_IFNE,
8, "ifne",
NULL,

len use def prec format */


1,
1,
1,
1,
1,
1,
3,
3,
3,

0,
0,
1,
1,
1,
1,
0,
1,
1,

0,
1,
0,
1,
0,
0,
0,
0,
0,

0,
0,
0,
0,
0,
0,
0,
0,
0,

JOF_BYTE)
JOF_BYTE)
JOF_BYTE)
JOF_BYTE)
JOF_BYTE)
JOF_BYTE)
JOF_JUMP)
JOF_JUMP)
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_BY
TE)
/* ECMA-compliant for-in loop with argument or local variable loop control. */
OPDEF(JSOP_FORARG,
10, "forarg",
NULL,
3, 0, 1, 0, JOF_QARG|J
OF_NAME|JOF_SET)
OPDEF(JSOP_FORVAR,
11, "forvar",
NULL,
3, 0, 1, 0, JOF_QVAR|J
OF_NAME|JOF_SET)
/* More longstanding bytecodes. */
OPDEF(JSOP_DUP,
12, "dup",
NULL,
OPDEF(JSOP_DUP2,
13, "dup2",
NULL,
OPDEF(JSOP_SETCONST, 14, "setconst", NULL,
JOF_NAME|JOF_SET)
OPDEF(JSOP_BITOR,
15, "bitor",
"|",
OPDEF(JSOP_BITXOR,
16, "bitxor",
"^",
OPDEF(JSOP_BITAND,
17, "bitand",
"&",
OPDEF(JSOP_EQ,
18, "eq",
"==",
OPDEF(JSOP_NE,
19, "ne",
"!=",
OPDEF(JSOP_LT,
20, "lt",
"<",
OPDEF(JSOP_LE,
21, "le",
"<=",
OPDEF(JSOP_GT,
22, "gt",
">",
OPDEF(JSOP_GE,
23, "ge",
">=",
OPDEF(JSOP_LSH,
24, "lsh",
"<<",
OPDEF(JSOP_RSH,
25, "rsh",
">>",
OPDEF(JSOP_URSH,
26, "ursh",
">>>",
OPDEF(JSOP_ADD,
27, "add",
"+",
OPDEF(JSOP_SUB,
28, "sub",
"-",
OPDEF(JSOP_MUL,
29, "mul",
"*",
OPDEF(JSOP_DIV,
30, "div",
"/",
OPDEF(JSOP_MOD,
31, "mod",
"%",
OPDEF(JSOP_NOT,
32, "not",
"!",
OPDEF(JSOP_BITNOT,
33, "bitnot",
"~",
OPDEF(JSOP_NEG,
34, "neg",
"-",
OPDEF(JSOP_NEW,
35, js_new_str, NULL,
)
OPDEF(JSOP_DELNAME, 36, "delname",
NULL,
JOF_NAME|JOF_DEL)
OPDEF(JSOP_DELPROP, 37, "delprop",
NULL,
JOF_PROP|JOF_DEL)
OPDEF(JSOP_DELELEM, 38, "delelem",
NULL,
JOF_ELEM|JOF_DEL)
OPDEF(JSOP_TYPEOF,
39, js_typeof_str,NULL,
OPDEF(JSOP_VOID,
40, js_void_str, NULL,
OPDEF(JSOP_INCNAME, 41, "incname",
NULL,
JOF_NAME|JOF_INC)
OPDEF(JSOP_INCPROP, 42, "incprop",
NULL,
JOF_PROP|JOF_INC)
OPDEF(JSOP_INCELEM, 43, "incelem",
NULL,
JOF_ELEM|JOF_INC)
OPDEF(JSOP_DECNAME, 44, "decname",
NULL,
JOF_NAME|JOF_DEC)
OPDEF(JSOP_DECPROP, 45, "decprop",
NULL,
JOF_PROP|JOF_DEC)
OPDEF(JSOP_DECELEM, 46, "decelem",
NULL,
JOF_ELEM|JOF_DEC)
OPDEF(JSOP_NAMEINC, 47, "nameinc",
NULL,
JOF_NAME|JOF_INC|JOF_POST)
OPDEF(JSOP_PROPINC, 48, "propinc",
NULL,

1, 1, 2, 0, JOF_BYTE)
1, 2, 4, 0, JOF_BYTE)
3, 1, 1, 1, JOF_CONST|
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
3,

2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
1,
1,
1,
-1,

1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,

2,
3,
4,
5,
5,
6,
6,
6,
6,
7,
7,
7,
8,
8,
9,
9,
9,
10,
10,
10,
10,

JOF_BYTE)
JOF_BYTE)
JOF_BYTE)
JOF_BYTE)
JOF_BYTE)
JOF_BYTE)
JOF_BYTE)
JOF_BYTE)
JOF_BYTE)
JOF_BYTE)
JOF_BYTE)
JOF_BYTE)
JOF_BYTE)
JOF_BYTE)
JOF_BYTE)
JOF_BYTE)
JOF_BYTE)
JOF_BYTE)
JOF_BYTE)
JOF_BYTE)
JOF_UINT16

3, 0, 1, 10, JOF_CONST|
3, 1, 1, 10, JOF_CONST|
1, 2, 1, 10, JOF_BYTE |
1, 1, 1, 10, JOF_BYTE)
1, 1, 1, 10, JOF_BYTE)
3, 0, 1, 10, JOF_CONST|
3, 1, 1, 10, JOF_CONST|
1, 2, 1, 10, JOF_BYTE |
3, 0, 1, 10, JOF_CONST|
3, 1, 1, 10, JOF_CONST|
1, 2, 1, 10, JOF_BYTE |
3, 0, 1, 10, JOF_CONST|
3, 1, 1, 10, JOF_CONST|

JOF_PROP|JOF_INC|JOF_POST)
OPDEF(JSOP_ELEMINC, 49, "eleminc",
JOF_ELEM|JOF_INC|JOF_POST)
OPDEF(JSOP_NAMEDEC, 50, "namedec",
JOF_NAME|JOF_DEC|JOF_POST)
OPDEF(JSOP_PROPDEC, 51, "propdec",
JOF_PROP|JOF_DEC|JOF_POST)
OPDEF(JSOP_ELEMDEC, 52, "elemdec",
JOF_ELEM|JOF_DEC|JOF_POST)
OPDEF(JSOP_GETPROP, 53, "getprop",
JOF_PROP)
OPDEF(JSOP_SETPROP, 54, "setprop",
JOF_PROP|JOF_SET)
OPDEF(JSOP_GETELEM, 55, "getelem",
JOF_ELEM)
OPDEF(JSOP_SETELEM, 56, "setelem",
JOF_ELEM|JOF_SET)
OPDEF(JSOP_PUSHOBJ, 57, "pushobj",
OPDEF(JSOP_CALL,
58, "call",
)
OPDEF(JSOP_NAME,
59, "name",
JOF_NAME)
OPDEF(JSOP_NUMBER,
60, "number",
OPDEF(JSOP_STRING,
61, "string",
OPDEF(JSOP_ZERO,
62, "zero",
OPDEF(JSOP_ONE,
63, "one",
OPDEF(JSOP_NULL,
64, js_null_str,
OPDEF(JSOP_THIS,
65, js_this_str,
OPDEF(JSOP_FALSE,
66, js_false_str,
OPDEF(JSOP_TRUE,
67, js_true_str,
OPDEF(JSOP_OR,
68, "or",
OPDEF(JSOP_AND,
69, "and",

NULL,

1, 2, 1, 10, JOF_BYTE |

NULL,

3, 0, 1, 10, JOF_CONST|

NULL,

3, 1, 1, 10, JOF_CONST|

NULL,

1, 2, 1, 10, JOF_BYTE |

NULL,

3, 1, 1, 11, JOF_CONST|

NULL,

3, 2, 1, 1, JOF_CONST|

NULL,

1, 2, 1, 11, JOF_BYTE |

NULL,

1, 3, 1, 1, JOF_BYTE |

NULL,
NULL,

1, 0, 1, 0, JOF_BYTE)
3, -1, 1, 11, JOF_UINT16

NULL,

3, 0, 1, 12, JOF_CONST|

NULL,
NULL,
"0",
"1",
js_null_str,
js_this_str,
js_false_str,
js_true_str,
NULL,
NULL,

3,
3,
1,
1,
1,
1,
1,
1,
3,
3,

/* The switch bytecodes have variable length. */


OPDEF(JSOP_TABLESWITCH, 70, "tableswitch", NULL,
WITCH)
OPDEF(JSOP_LOOKUPSWITCH, 71, "lookupswitch", NULL,
SWITCH)

0,
0,
0,
0,
0,
0,
0,
0,
1,
1,

1,
1,
1,
1,
1,
1,
1,
1,
0,
0,

12,
12,
12,
12,
12,
12,
12,
12,
0,
0,

JOF_CONST)
JOF_CONST)
JOF_BYTE)
JOF_BYTE)
JOF_BYTE)
JOF_BYTE)
JOF_BYTE)
JOF_BYTE)
JOF_JUMP)
JOF_JUMP)

-1, 1, 0, 0, JOF_TABLES
-1, 1, 0, 0, JOF_LOOKUP

/* New, infallible/transitive identity ops. */


OPDEF(JSOP_NEW_EQ,
72, "eq",
NULL,
OPDEF(JSOP_NEW_NE,
73, "ne",
NULL,

1, 2, 1, 5, JOF_BYTE)
1, 2, 1, 5, JOF_BYTE)

/* Lexical closure constructor. */


OPDEF(JSOP_CLOSURE, 74, "closure",

NULL,

3, 0, 0, 0, JOF_CONST)

*/
"exportall", NULL,
"exportname", NULL,

1, 0, 0, 0, JOF_BYTE)
3, 0, 0, 0, JOF_CONST|

"importall", NULL,
"importprop", NULL,

1, 1, 0, 0, JOF_BYTE)
3, 1, 0, 0, JOF_CONST|

"importelem", NULL,

1, 2, 0, 0, JOF_BYTE |

/* Export and import ops.


OPDEF(JSOP_EXPORTALL, 75,
OPDEF(JSOP_EXPORTNAME,76,
JOF_NAME)
OPDEF(JSOP_IMPORTALL, 77,
OPDEF(JSOP_IMPORTPROP,78,
JOF_PROP|JOF_IMPORT)
OPDEF(JSOP_IMPORTELEM,79,
JOF_ELEM|JOF_IMPORT)

/* Push object literal. */


OPDEF(JSOP_OBJECT,
80, "object",
/* Pop value and discard it. */

NULL,

3, 0, 1, 12, JOF_CONST)

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_NAME)
OPDEF(JSOP_SETARG,
85, "setarg",
NULL,
3, 1, 1, 1,
JOF_NAME|JOF_SET)
OPDEF(JSOP_GETVAR,
86, "getvar",
NULL,
3, 0, 1, 12,
JOF_NAME)
OPDEF(JSOP_SETVAR,
87, "setvar",
NULL,
3, 1, 1, 1,
JOF_NAME|JOF_SET)
/* Push unsigned 16-bit int constant. */
OPDEF(JSOP_UINT16,
88, "uint16",
NULL,
)
/* Object and array literal support. */
OPDEF(JSOP_NEWINIT, 89, "newinit",
OPDEF(JSOP_ENDINIT, 90, "endinit",
OPDEF(JSOP_INITPROP, 91, "initprop",
JOF_PROP)
OPDEF(JSOP_INITELEM, 92, "initelem",
JOF_ELEM)
OPDEF(JSOP_DEFSHARP, 93, "defsharp",
)
OPDEF(JSOP_USESHARP, 94, "usesharp",
)
/* Fast inc/dec ops for args and local
OPDEF(JSOP_INCARG,
95, "incarg",
JOF_NAME|JOF_INC)
OPDEF(JSOP_INCVAR,
96, "incvar",
JOF_NAME|JOF_INC)
OPDEF(JSOP_DECARG,
97, "decarg",
JOF_NAME|JOF_DEC)
OPDEF(JSOP_DECVAR,
98, "decvar",
JOF_NAME|JOF_DEC)
OPDEF(JSOP_ARGINC,
99, "arginc",
JOF_NAME|JOF_INC|JOF_POST)
OPDEF(JSOP_VARINC,
100,"varinc",
JOF_NAME|JOF_INC|JOF_POST)
OPDEF(JSOP_ARGDEC,
101,"argdec",
JOF_NAME|JOF_DEC|JOF_POST)
OPDEF(JSOP_VARDEC,
102,"vardec",
JOF_NAME|JOF_DEC|JOF_POST)
/* ECMA-compliant for/in ops. */
OPDEF(JSOP_TOOBJECT, 103,"toobject",
OPDEF(JSOP_FORNAME, 104,"forname",
JOF_NAME|JOF_SET)
OPDEF(JSOP_FORPROP, 105,"forprop",
JOF_PROP|JOF_SET)
OPDEF(JSOP_FORELEM, 106,"forelem",
JOF_ELEM)

JOF_QARG |
JOF_QARG |
JOF_QVAR |
JOF_QVAR |

3, 0, 1, 12, JOF_UINT16

NULL,
NULL,
NULL,

1, 2, 1, 10, JOF_BYTE)
1, 0, 0, 0, JOF_BYTE)
3, 1, 0, 0, JOF_CONST|

NULL,

1, 2, 0, 0, JOF_BYTE |

NULL,

3, 0, 0, 0, JOF_UINT16

NULL,

3, 0, 1, 0, JOF_UINT16

vars. */
NULL,

3, 0, 1, 10, JOF_QARG |

NULL,

3, 0, 1, 10, JOF_QVAR |

NULL,

3, 0, 1, 10, JOF_QARG |

NULL,

3, 0, 1, 10, JOF_QVAR |

NULL,

3, 0, 1, 10, JOF_QARG |

NULL,

3, 0, 1, 10, JOF_QVAR |

NULL,

3, 0, 1, 10, JOF_QARG |

NULL,

3, 0, 1, 10, JOF_QVAR |

NULL,
NULL,

1, 1, 1, 0, JOF_BYTE)
3, 0, 1, 0, JOF_CONST|

NULL,

3, 1, 1, 0, JOF_CONST|

NULL,

1, 2, 4, 0, JOF_BYTE |

OPDEF(JSOP_POP2,

107,"pop2",

/* ECMA-compliant assignment ops. */


OPDEF(JSOP_BINDNAME, 108,"bindname",
JOF_NAME)
OPDEF(JSOP_SETNAME, 109,"setname",
JOF_NAME|JOF_SET)
/* Exception handling ops. */
OPDEF(JSOP_THROW,
110,"throw",

NULL,

1, 2, 0, 0, JOF_BYTE)

NULL,

3, 0, 1, 0, JOF_CONST|

NULL,

3, 2, 1, 1, JOF_CONST|

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,
OPDEF(JSOP_RETSUB,
115,"retsub",
NULL,

3, 0, 1, 0, JOF_JUMP)
1, 1, 0, 0, JOF_BYTE)

/* More exception handling ops. */


OPDEF(JSOP_EXCEPTION, 116,"exception", NULL,
OPDEF(JSOP_SETSP,
117,"setsp",
NULL,
)

1, 0, 1, 0, JOF_BYTE)
3, 0, 0, 0, JOF_UINT16

/*
* ECMA-compliant switch statement ops.
* CONDSWITCH is a decompilable NOP; CASE is ===, POP, jump if true,
* lval if false; and DEFAULT is POP lval and GOTO.
*/
OPDEF(JSOP_CONDSWITCH,118,"condswitch", NULL,
1, 0, 0, 0,
OPDEF(JSOP_CASE,
119,"case",
NULL,
3, 1, 0, 0,
OPDEF(JSOP_DEFAULT, 120,"default",
NULL,
3, 1, 0, 0,
/*
* ECMA-compliant call to eval op
*/
OPDEF(JSOP_EVAL,
121,"eval",
)

NULL,

re-push
JOF_BYTE)
JOF_JUMP)
JOF_JUMP)

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",
JOF_NAME|JOF_SET)
OPDEF(JSOP_DEFVAR,
127,"defvar",
JOF_NAME)

NULL,

3, 0, 0, 0, JOF_CONST|

NULL,

3, 0, 0, 0, JOF_CONST|

/* 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 registe
r, 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 JSO
P_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
* JSOP_ARGSUB gets arguments[i] from fp->argv, iff i
* JSOP_ARGCNT returns fp->argc.
*/
OPDEF(JSOP_ARGSUB,
136,"argsub",
NULL,
JOF_NAME)
OPDEF(JSOP_ARGCNT,
137,"argcnt",
NULL,

most cases:
is in [0, fp->argc-1].
3, 0, 1, 12, JOF_QARG |
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_DEFLOC
ALVAR)
**** 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
#define SET_BIT
#define CLEAR_BIT

JS_TEST_BIT
JS_SET_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
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#endif

/* Mac standard is lower case true */


/* Mac standard is lower case false */

#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
static
static
static
#endif

METER_PARSENODES
uint32 parsenodes = 0;
uint32 maxparsenodes = 0;
uint32 recyclednodes = 0;

/*
* 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_define
FunctionsOnCompile
// then define the property now - this is because we need to com
pile scripts
// in the document, so we can execute some of the functions sele
ctively 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 functi
on
// definition, but we can't just execute any old script, we only
execute
// specific behavior functions that we define and have control o
ver.
//
// The following code that defines the property is copied and mo
dified 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_defineFunctionsOnCom
pile", &specialPropVal) &&
JS_TRUE == JSVAL_TO_BOOLEAN(specialPropV
al) )
{
/*
* We must be at top-level (default "box", eithe
r function body or
* global) scope, not inside a with or other com
pound 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 var
iables 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 i
n fp->varobj.
*
* If static link is not current scope, clone fu
n'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 amon
g the scopes and
* amortizing it over a number of executions. E
xamples include XUL
* scripts and event handlers shared among Mozil
la chrome windows,
* and server-side JS user-defined functions sha
red among requests.
*
* NB: The Script object exposes compile and exe
c in the language,
* such that this clause introduces an incompati
ble change from old
* JS versions that supported Script. Such a JS
version supported
* executing a script that defined and called fu
nctions scoped by
* the compile-time static link, not by the exec
-time scope chain.
*
* We sacrifice compatibility, breaking such scr
ipts, 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, pa
rent);
if (!obj) {
ok = JS_FALSE;
goto out;
}
}
*****/
/* Load function flags that are also property at
tributes. */
flags = fun->flags & (JSFUN_GETTER | JSFUN_SETTE
R);
attrs = flags | JSPROP_ENUMERATE;
/*
* Check for a const property of the same name - or any kind
* of property if executing with the strict opti
on. We check
* here at runtime as well as at compile-time, t
o handle eval
* as well as multiple HTML script tags.
*/
parent = fp->varobj;
if (js_CheckRedeclaration(cx, parent, (jsid)funA
tom, attrs, &cond))
{
ok = OBJ_DEFINE_PROPERTY(cx, parent, (js
id)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
/*
pn
if

TOK_IF:
An IF node has three kids: condition, then, and optional else. */
= NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);
(!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
switch
case
if

= js_GetToken(cx, ts)) != TOK_RC) {


(tt) {
TOK_DEFAULT:
(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
/*
pn
if

TOK_FOR:
A FOR node is binary, left is loop control and right is the body. */
= NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
(!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
case
pn
if

TOK_EOL:
TOK_SEMI:
= NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
(!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_GetLocalVariabl
e);
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
case
case
case
pn
if

(tt) {
TOK_UNARYOP:
TOK_PLUS:
TOK_MINUS:
= NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
(!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
case
pn
if

TOK_INC:
TOK_DEC:
= NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
(!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
/*
pn
if

TOK_USESHARP:
Check for forward/dangling references at runtime, to allow eval. */
= NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
(!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
case
pn
if

TOK_NAME:
TOK_OBJECT:
= NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
(!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,
return JS_FALSE;
if (pn2 && !js_FoldConstants(cx,
return JS_FALSE;
if (pn3 && !js_FoldConstants(cx,
return JS_FALSE;
break;

(;;)). */

pn1, tc))
pn2, tc))
pn3, tc))

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
case
case
if

TOK_SHOP:
TOK_MINUS:
TOK_DIVOP:
(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
----------<Definitions>
TOK_FUNCTION func

Members
------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
TOK_EXPORT list
TOK_IMPORT

list

TOK_IF
TOK_SWITCH

ternary
binary

TOK_CASE,
TOK_DEFAULT
TOK_WHILE
TOK_DO
TOK_FOR

binary

TOK_THROW
TOK_TRY

unary
ternary

TOK_CATCH

ternary

TOK_BREAK
TOK_CONTINUE
TOK_WITH
TOK_VAR

name
name
binary
list

TOK_RETURN
TOK_SEMI
TOK_COLON

unary
unary
name

binary
binary
binary

<Expressions>
TOK_COMMA
list
TOK_ASSIGN binary
TOK_HOOK
TOK_OR
TOK_AND
TOK_BITOR

ternary
binary
binary
binary

pn_head: list of pn_count statements


pn_head: list of pn_count TOK_NAMEs or one TOK_STAR
(which is not a multiply node)
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}
pn_kid1: cond, pn_kid2: then, pn_kid3: else or null
pn_left: discriminant
pn_right: list of TOK_CASE nodes, with at most one
TOK_DEFAULT node
pn_left: case expr or null if TOK_DEFAULT
pn_right: TOK_LC node for this case's statements
pn_left: cond, pn_right: body
pn_left: body, pn_right: cond
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
pn_op: JSOP_THROW, pn_kid: exception
pn_kid1: try block
pn_kid2: catch blocks or null
pn_kid3: finally block or null
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
pn_atom: label or null
pn_atom: label or null
pn_left: head expr, pn_right: body
pn_head: list of pn_count TOK_NAME nodes
each name node has pn_atom: variable name and
pn_expr: initializer or null
pn_kid: return expr or null
pn_kid: expr or null statement
pn_atom: label, pn_expr: labeled statement
pn_head: list of pn_count comma-separated exprs
pn_left: lvalue, pn_right: rvalue
pn_op: JSOP_ADD for +=, etc.
pn_kid1: cond, pn_kid2: then, pn_kid3: else
pn_left: first in || chain, pn_right: rest of chain
pn_left: first in && chain, pn_right: rest of chain
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
JSTokenPos

pn_type;
pn_pos;

JSOp
ptrdiff_t
JSParseNodeArity
union {
struct {
JSFunction
JSParseNode
uint32
uint32
} func;
struct {
JSParseNode
JSParseNode
uint32
JSBool
} list;
struct {
JSParseNode
JSParseNode
JSParseNode
} ternary;
struct {
JSParseNode
JSParseNode
jsval
} binary;
struct {
JSParseNode
jsint
} unary;
struct {
JSAtom
JSParseNode
jsint
uintN
} name;
jsdouble
} pn_u;
JSParseNode

pn_op;
pn_offset;
pn_arity;

/* first generated bytecode offset */

*fun;
*body;
flags;
tryCount;

/*
/*
/*
/*
/*

TOK_FUNCTION node */
function object private data */
TOK_LC list of statements */
accumulated tree context flags */
count of try statements in body */

*head;
**tail;
count;
extra;

/*
/*
/*
/*
/*

list of next-linked nodes */


first node in list */
ptr to ptr to last node in list */
number of nodes in list */
extra comma flag for [1,2,,] */

*kid1;
*kid2;
*kid3;

/*
/*
/*
/*

ternary: if, for(;;), ?: */


condition, discriminant, etc. */
then-part, case list, etc. */
else-part, default case, etc. */

/* two kids if binary */


*left;
*right;
val;

/* switch case value */


/* one kid if unary */

*kid;
num;

/* -1 or sharp variable number */

*atom;
*expr;
slot;
attrs;

/*
/*
/*
/*
/*

dval;

/* aligned numeric literal value */

*pn_next;

/* to align dval and pn_u on RISCs */

};
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define

pn_fun
pn_body
pn_flags
pn_tryCount
pn_head
pn_tail
pn_count
pn_extra
pn_kid1
pn_kid2
pn_kid3
pn_left
pn_right
pn_val
pn_kid
pn_num
pn_atom
pn_expr
pn_slot

pn_u.func.fun
pn_u.func.body
pn_u.func.flags
pn_u.func.tryCount
pn_u.list.head
pn_u.list.tail
pn_u.list.count
pn_u.list.extra
pn_u.ternary.kid1
pn_u.ternary.kid2
pn_u.ternary.kid3
pn_u.binary.left
pn_u.binary.right
pn_u.binary.val
pn_u.unary.kid
pn_u.unary.num
pn_u.name.atom
pn_u.name.expr
pn_u.name.slot

name, labeled statement, etc. */


name or label atom, null if slot */
object or initializer */
-1 or arg or local var slot */
attributes if local var or const */

#define pn_attrs
#define pn_dval

pn_u.name.attrs
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
#include
#include
#include
#include
#include

<stdio.h>
<string.h>
<stdlib.h>
"jsprf.h"
"jslong.h"
"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;
va_list ap;
};

/* type of the current 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
#define
#define
#define
#define
#define
#define
#define

TYPE_INT16
TYPE_UINT16
TYPE_INTN
TYPE_UINTN
TYPE_INT32
TYPE_UINT32
TYPE_INT64
TYPE_UINT64

0
1
2
3
4
5
6
7

#define
#define
#define
#define

TYPE_STRING
TYPE_DOUBLE
TYPE_INTSTR
TYPE_UNKNOWN

8
9
10
20

#define
#define
#define
#define
#define

_LEFT
_SIGNED
_SPACED
_ZEROS
_NEG

0x1
0x2
0x4
0x8
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)) {
if (flags & _ZEROS) {
space = '0';
}
while (--width >= 0) {
rv = (*ss->stuff)(ss, &space, 1);
if (rv < 0) {
return rv;
}
}
}

/* Right adjusting */

/* Copy out the source data */


rv = (*ss->stuff)(ss, src, (JSUint32)srclen);
if (rv < 0) {
return rv;
}
if ((width > 0) && ((flags & _LEFT) != 0)) {
while (--width >= 0) {
rv = (*ss->stuff)(ss, &space, 1);
if (rv < 0) {
return rv;
}
}
}
return 0;

/* Left adjusting */

}
/*
** 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;
cvtwidth += precwidth;
}
}
if ((flags & _ZEROS) && (prec < 0)) {
if (width > cvtwidth) {
zerowidth = width - cvtwidth;
cvtwidth += zerowidth;
}
}

/* Need zero filling */

/* Zero filling */

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++ ) == '%' )
continue;

/* skip %% case */

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 != '$' ){
cn = cn*10 + c - '0';
c = *p++;
}

/* should imporve error check later */

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 argu
ments \"%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
case
case
case

'C':
'S':
'E':
'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 );

case TYPE_INT32:

(void)va_arg( ap, JSInt32 );

break;
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':
radix = 10;
goto fetch_and_convert;

/* decimal/integer */

case 'o':
radix = 8;
type |= 1;
goto fetch_and_convert;

/* octal */

case 'u':
radix = 10;
type |= 1;
goto fetch_and_convert;

/* unsigned decimal */

case 'x':
radix = 16;
type |= 1;
goto fetch_and_convert;

/* unsigned hex */

case 'X':
radix = 16;
hexp = HEX;
type |= 1;
goto fetch_and_convert;

/* unsigned HEX */

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 cha
r *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 ch


ar *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_l
ist 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 v
oid *v2);
typedef JSPRIntn (JSJS_DLL_CALLBACK *JSPRHashEnumerator)(JSPRHashEntry *he, JSPR
Intn i, void *arg);
/* Flag
#define
#define
#define
#define

bits in JSPRHashEnumerator's return value */


HT_ENUMERATE_NEXT
0
/* continue enumerating entries */
HT_ENUMERATE_STOP
1
/* stop enumerating entries */
HT_ENUMERATE_REMOVE
2
/* remove and free the current entry */
HT_ENUMERATE_UNHASH
4
/* just unhash the current entry */

typedef struct JSPRHashAllocOps {


void *
(JSJS_DLL_CALLBACK *allocTable)(void *pool, JSPRSize siz
e);
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
#define HT_FREE_ENTRY

0
1

/* just free the entry's value */


/* free value and entire entry */

struct JSPRHashEntry {
JSPRHashEntry
*next;
JSPRHashNumber
keyHash;
const void
*key;
void
*value;
};
struct JSPRHashTable {
JSPRHashEntry
JSPRUint32
JSPRUint32

**buckets;
nentries;
shift;

/* hash chain linkage */


/* key hash function result */
/* ptr to opaque key */
/* ptr to opaque value */

/* vector of hash buckets */


/* number of entries in table */
/* 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 *k
ey);
JSJS_EXTERN_API(JSPRHashEntry *)
JSPR_HashTableRawAdd(JSPRHashTable *ht, JSPRHashEntry **hep, JSPRHashNumber keyH
ash,
const void *key, void *value);
JSJS_EXTERN_API(void)
JSPR_HashTableRawRemove(JSPRHashTable *ht, JSPRHashEntry **hep, JSPRHashEntry *h
e);
/* 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 *ar
g);
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
typedef struct JSCodeGenerator
typedef struct JSGCThing
typedef struct JSParseNode
typedef struct JSSharpObjectMap
typedef struct JSToken
typedef struct JSTokenPos
typedef struct JSTokenPtr
typedef struct JSTokenStream
typedef struct JSTreeContext
typedef struct JSTryNote
/* Friend "Advanced API" typedefs.
typedef struct JSAtom
typedef struct JSAtomList
typedef struct JSAtomListElement
typedef struct JSAtomMap
typedef struct JSAtomState
typedef struct JSCodeSpec
typedef struct JSPrinter
typedef struct JSRegExp
typedef struct JSRegExpStatics
typedef struct JSScope
typedef struct JSScopeOps
typedef struct JSScopeProperty
typedef struct JSStackFrame
typedef struct JSStackHeader
typedef struct JSSubString
typedef struct JSSymbol

JSArgumentFormatMap;
JSCodeGenerator;
JSGCThing;
JSParseNode;
JSSharpObjectMap;
JSToken;
JSTokenPos;
JSTokenPtr;
JSTokenStream;
JSTreeContext;
JSTryNote;
*/
JSAtom;
JSAtomList;
JSAtomListElement;
JSAtomMap;
JSAtomState;
JSCodeSpec;
JSPrinter;
JSRegExp;
JSRegExpStatics;
JSScope;
JSScopeOps;
JSScopeProperty;
JSStackFrame;
JSStackHeader;
JSSubString;
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
const char
uintN
JSScript
JSFunction
void

*cx,
*filename, /* URL of script */
lineno,
/* line script starts */
*script,
*fun,
*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
typedef struct JSConstDoubleSpec
typedef struct JSContext
typedef struct JSErrorReport
typedef struct JSFunction

JSClass;
JSConstDoubleSpec;
JSContext;
JSErrorReport;
JSFunction;

typedef
typedef
typedef
typedef
typedef
typedef
typedef
typedef
typedef
typedef
typedef
typedef
typedef
typedef

struct
struct
struct
struct
struct
struct
struct
struct
struct
struct
struct
struct
struct
struct

JSFunctionSpec
JSIdArray
JSProperty
JSPropertySpec
JSObject
JSObjectMap
JSObjectOps
JSRuntime
JSRuntime
JSScript
JSString
JSXDRState
JSExceptionState
JSLocaleCallbacks

JSFunctionSpec;
JSIdArray;
JSProperty;
JSPropertySpec;
JSObject;
JSObjectMap;
JSObjectOps;
JSRuntime;
JSTaskState; /* XXX deprecated name */
JSScript;
JSString;
JSXDRState;
JSExceptionState;
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, jsva
l *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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

<string.h>
"jstypes.h"
"jsarena.h" /* Added by JSIFY */
"jsutil.h" /* Added by JSIFY */
"jsapi.h"
"jsarray.h"
"jsatom.h"
"jscntxt.h"
"jsconfig.h"
"jsfun.h"
"jsgc.h"
"jsinterp.h"
"jslock.h"
"jsnum.h"
"jsobj.h"
"jsopcode.h"
"jsregexp.h"
"jsscan.h"
"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
REOP_JUMP
REOP_DOTSTAR
REOP_ANCHOR

=
=
=
=

22,
23,
24,
25,

/*
/*
/*
/*

match a single char */


for deoptimized closure loops */
optimize .* to use a single opcode */
like .* but skips left context to unanchored r.e. *

REOP_EOLONLY
REOP_UCFLAT
REOP_UCFLAT1
REOP_UCCLASS

=
=
=
=

26,
27,
28,
29,

/*
/*
/*
/*

$ not preceded by any pattern */


flat Unicode string; len immediate counts chars */
single Unicode char */
Unicode character class, vector of chars to match *

REOP_NUCCLASS
REOP_BACKREFi
REOP_FLATi
REOP_FLAT1i
REOP_UCFLATi
REOP_UCFLAT1i
REOP_ANCHOR1
REOP_NCCLASS
REOP_DOTSTARMIN
REOP_LPARENNON
REOP_RPARENNON
REOP_ASSERT
REOP_ASSERT_NOT
REOP_END
} REOp;

=
=
=
=
=
=
=
=
=
=
=
=
=

30,
31,
32,
33,
34,
35,
36,
37,
38,
39,
40,
41,
42,

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

negated Unicode character class */


case-independent REOP_BACKREF */
case-independent REOP_FLAT */
case-independent REOP_FLAT1 */
case-independent REOP_UCFLAT */
case-independent REOP_UCFLAT1 */
first-char discriminating REOP_ANCHOR */
negated 8-bit character class */
ungreedy version of REOP_DOTSTAR */
non-capturing version of REOP_LPAREN */
non-capturing version of REOP_RPAREN */
zero width positive lookahead assertion */
zero width negative lookahead assertion */

#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


#define RENODE_SINGLE 0x02

/* anchored at the front */


/* matches a single char */

#define
#define
#define
#define
#define
#define

RENODE_NONEMPTY
RENODE_ISNEXT
RENODE_GOODNEXT
RENODE_ISJOIN
RENODE_REALLOK
RENODE_MINIMAL

0x04
0x08
0x10
0x20
0x40
0x80

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

does not match empty string */


ren is next after at least one node */
ren->next is a tree-like edge in the graph */
ren is a join point in the graph */
REOP_FLAT owns tempPool space to realloc */
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
static
static
static
static

REParser
REParser
REParser
REParser
REParser

ParseRegExp;
ParseAltern;
ParseItem;
ParseQuantAtom;
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
*
item altern
*/
static RENode *
ParseAltern(CompilerState *state)
{
RENode *ren, *ren1, *ren2;
uintN flags;
const jschar *cp;
jschar c;

An alternative is one or more items,


concatenated together.

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
*
quantatom
*
* assertion: '^'
*
*
*
'$'
*
*
*
'\b'
*
'\B'
*/
static RENode *
ParseItem(CompilerState *state)
{
const jschar *cp;
RENode *ren;
REOp op;

An item is either an assertion or


a quantified atom.
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).
Word boundary (between \w and \W).
Word non-boundary.

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 th
ey

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


'.'
'[' classlist ']'
'[' '^' classlist ']'
'\f'
'\n'
'\r'
'\t'
'\v'
'\d'
'\D'
'\w'
'\W'
'\s'
'\S'
'\' n
'\' octal
'\x' hex
'\c' ctrl
'\' literalatomchar

A parenthesized regexp (what matched


can be addressed using a backreference,
see '\' n below).
Matches any char except '\n'.
A character class.
A negated character class.
Form Feed.
Newline (Line Feed).
Carriage Return.
Horizontal Tab.
Vertical Tab.
A digit (same as [0-9]).
A non-digit.
A word character, [0-9a-z_A-Z].
A non-word character.
A whitespace character, [ \b\f\n\r\t\v].
A non-whitespace character.
A backreference to the nth (n decimal
and positive) parenthesized expression.
An octal escape sequence (octal must be
two or three digits long, unless it is
0 for the null character).
A hex escape (hex must be two digits).
A control character, ctrl is a letter.
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;
num = 'x';

/* \xZZ is xZZ (Perl does \0ZZ!) */

}
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;
JSBool
anchoring;
jsbytecode
*pcend;
const jschar
*cpbegin, *cpend;
size_t
start;
ptrdiff_t
skipped;
uint8
flags;
uintN
parenCount;
JSSubString
*maybeParens;
JSSubString
*parens;
JSBool
ok;
ng */
const jschar
*complete;
} MatchState;

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

for access to regExpStatics */


true if multiline anchoring ^/$ */
pc limit (fencepost) */
cp base address and limit */
offset from cpbegin to start at */
chars skipped anchoring this r.e. */
flags */
number of paren substring matches */
possible paren substring pointers */
certain paren substring matches */
indicates runtime error during matchi

/*
* 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, previo
usKid);
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, previo
usKid);
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 *previousKi
d)
{
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 r
ange 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 alw
ays 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 spa
ce (just in case)
*cp2 == 0x00A0 ) //no break sp
ace
{
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
REGEXP_GLOBAL
REGEXP_IGNORE_CASE
REGEXP_LAST_INDEX
REGEXP_MULTILINE
};

=
=
=
=
=

-1,
-2,
-3,
-4,
-5

#define REGEXP_PROP_ATTRS (JSPROP_ENUMERATE|JSPROP_PERMANENT|JSPROP_SHARED)


static JSPropertySpec regexp_props[] =
{"source",
REGEXP_SOURCE,
{"global",
REGEXP_GLOBAL,
{"ignoreCase", REGEXP_IGNORE_CASE,
{"lastIndex", REGEXP_LAST_INDEX,
{"multiline", REGEXP_MULTILINE,
{0,0,0,0,0}
};

{
REGEXP_PROP_ATTRS | JSPROP_READONLY,0,0},
REGEXP_PROP_ATTRS | JSPROP_READONLY,0,0},
REGEXP_PROP_ATTRS | JSPROP_READONLY,0,0},
REGEXP_PROP_ATTRS,0,0},
REGEXP_PROP_ATTRS | JSPROP_READONLY,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,
JS_EnumerateStub, JS_ResolveStub,
NULL,
NULL,
regexp_xdrObject, NULL,
};

regexp_getProperty,
JS_ConvertStub,
regexp_call,
NULL,

regexp_setProperty,
regexp_finalize,
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,
#endif
{js_toString_str, regexp_toString,
{"compile",
regexp_compile,
{"exec",
regexp_exec,
{"test",
regexp_test,
{0,0,0,0,0}
};

0,0,0},
0,0,0},
1,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;
JSBool
multiline;
uintN
parenCount;
uintN
moreLength;
JSSubString parens[9];
JSSubString *moreParens;
JSSubString lastMatch;
JSSubString lastParen;
JSSubString leftContext;
JSSubString rightContext;
};

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

input string to match (perl $_, GC root) */


whether input contains newlines (perl $*) */
number of valid elements in parens[] */
number of allocated elements in moreParens */
last set of parens matched (perl $1, $2) */
null or realloc'd vector for $10, etc. */
last string matched (perl $&) */
last paren matched (perl $+) */
input to left of last match (perl $`) */
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 becau
se
* 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;
uintN
parenCount;
uint8
flags;
struct RENode *ren;

/*
/*
/*
/*

index after last match, for //g iterator */


number of parenthesized submatches */
flags, see jsapi.h */
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",
{"with",
#if JS_HAS_CONST
{js_const_str,
,
#else
{js_const_str,
#endif
#if JS_HAS_EXCEPTIONS
{"try",
{"catch",
{"finally",
{"throw",
#else
{"try",
{"catch",
{"finally",
{"throw",
#endif

TOK_WHILE,
TOK_WITH,

JSOP_NOP,
JSOP_NOP,

TOK_VAR,

JSOP_DEFCONST,JSVERSION_DEFAULT}

TOK_RESERVED,

JSOP_NOP,

JSVERSION_DEFAULT},

TOK_TRY,
TOK_CATCH,
TOK_FINALLY,
TOK_THROW,

JSOP_NOP,
JSOP_NOP,
JSOP_NOP,
JSOP_NOP,

JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},

TOK_RESERVED,
TOK_RESERVED,
TOK_RESERVED,
TOK_RESERVED,

JSOP_NOP,
JSOP_NOP,
JSOP_NOP,
JSOP_NOP,

JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},

#if JS_HAS_INSTANCEOF
{js_instanceof_str, TOK_INSTANCEOF,
#else
{js_instanceof_str, TOK_RESERVED,
#endif

JSVERSION_DEFAULT},
JSVERSION_DEFAULT},

JSOP_INSTANCEOF,JSVERSION_1_4},
JSOP_NOP,

JSVERSION_1_4},

#ifdef RESERVE_JAVA_KEYWORDS
{"abstract",
TOK_RESERVED,
{"boolean",
TOK_RESERVED,
{"byte",
TOK_RESERVED,
{"char",
TOK_RESERVED,
{"class",
TOK_RESERVED,
{"double",
TOK_RESERVED,
{"extends",
TOK_RESERVED,
{"final",
TOK_RESERVED,
{"float",
TOK_RESERVED,
{"goto",
TOK_RESERVED,
{"implements",
TOK_RESERVED,
{"import",
TOK_IMPORT,
{"int",
TOK_RESERVED,
{"interface",
TOK_RESERVED,
{"long",
TOK_RESERVED,
{"native",
TOK_RESERVED,
{"package",
TOK_RESERVED,
{"private",
TOK_RESERVED,
{"protected",
TOK_RESERVED,
{"public",
TOK_RESERVED,
{"short",
TOK_RESERVED,
{"static",
TOK_RESERVED,
{"super",
TOK_RESERVED,
{"synchronized",
TOK_RESERVED,
{"throws",
TOK_RESERVED,
{"transient",
TOK_RESERVED,
{"volatile",
TOK_RESERVED,
#endif

JSOP_NOP,
JSOP_NOP,
JSOP_NOP,
JSOP_NOP,
JSOP_NOP,
JSOP_NOP,
JSOP_NOP,
JSOP_NOP,
JSOP_NOP,
JSOP_NOP,
JSOP_NOP,
JSOP_NOP,
JSOP_NOP,
JSOP_NOP,
JSOP_NOP,
JSOP_NOP,
JSOP_NOP,
JSOP_NOP,
JSOP_NOP,
JSOP_NOP,
JSOP_NOP,
JSOP_NOP,
JSOP_NOP,
JSOP_NOP,
JSOP_NOP,
JSOP_NOP,
JSOP_NOP,

JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},
JSVERSION_DEFAULT},

#ifdef RESERVE_ECMA_KEYWORDS
{"enum",
TOK_RESERVED,

JSOP_NOP,

JSVERSION_1_3},

#endif
#if JS_HAS_DEBUGGER_KEYWORD
{"debugger",
TOK_DEBUGGER,
#elif defined(RESERVE_ECMA_KEYWORDS)
{"debugger",
TOK_RESERVED,
#endif
{0,
TOK_EOF,
};

JSOP_NOP,

JSVERSION_1_3},

JSOP_NOP,

JSVERSION_1_3},

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

INIT_TOKENBUF(tb)
FINISH_TOKENBUF(tb)
TOKEN_LENGTH(tb)
RETURN(tt)

((tb)->ptr = (tb)->base)
if (!AddToTokenBuf(cx, tb, 0)) RETURN(TOK_ERROR)
((tb)->ptr - (tb)->base - 1)
{ 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
case
case
case
case
case
case
case
case
case

';':
'.':
'[':
']':
'{':
'}':
'(':
')':
',':
'?':

c
c
c
c
c
c
c
c
c
c

=
=
=
=
=
=
=
=
=
=

TOK_SEMI; break;
TOK_DOT; break;
TOK_LB; break;
TOK_RB; break;
TOK_LC; break;
TOK_RC; break;
TOK_LP; break;
TOK_RP; break;
TOK_COMMA; break;
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
#undef
#undef
#undef
}

INIT_TOKENBUF
FINISH_TOKENBUF
TOKEN_LENGTH
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,
TOK_EOF = 0,
TOK_EOL = 1,
TOK_SEMI = 2,
TOK_LB = 3, TOK_RB = 4,
TOK_LC = 5, TOK_RC = 6,
TOK_LP = 7, TOK_RP = 8,
TOK_COMMA = 9,
TOK_ASSIGN = 10,
TOK_HOOK = 11, TOK_COLON = 12,
TOK_OR = 13,
TOK_AND = 14,
TOK_BITOR = 15,
TOK_BITXOR = 16,

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

well-known as the only code < EOF */


end of file */
end of line */
semicolon */
left and right brackets */
left and right curlies (braces) */
left and right parentheses */
comma operator */
assignment ops (= += -= etc.) */
conditional (?:) */
logical or (||) */
logical and (&&) */
bitwise-or (|) */
bitwise-xor (^) */

TOK_BITAND = 17,
TOK_EQOP = 18,
TOK_RELOP = 19,
TOK_SHOP = 20,
TOK_PLUS = 21,
TOK_MINUS = 22,
TOK_STAR = 23, TOK_DIVOP = 24,
TOK_UNARYOP = 25,
TOK_INC = 26, TOK_DEC = 27,
TOK_DOT = 28,
TOK_NAME = 29,
TOK_NUMBER = 30,
TOK_STRING = 31,
TOK_OBJECT = 32,
TOK_PRIMARY = 33,
TOK_FUNCTION = 34,
TOK_EXPORT = 35,
TOK_IMPORT = 36,
TOK_IF = 37,
TOK_ELSE = 38,
TOK_SWITCH = 39,
TOK_CASE = 40,
TOK_DEFAULT = 41,
TOK_WHILE = 42,
TOK_DO = 43,
TOK_FOR = 44,
TOK_BREAK = 45,
TOK_CONTINUE = 46,
TOK_IN = 47,
TOK_VAR = 48,
TOK_WITH = 49,
TOK_RETURN = 50,
TOK_NEW = 51,
TOK_DELETE = 52,
TOK_DEFSHARP = 53,
TOK_USESHARP = 54,
TOK_TRY = 55,
TOK_CATCH = 56,
TOK_FINALLY = 57,
TOK_THROW = 58,
TOK_INSTANCEOF = 59,
TOK_DEBUGGER = 60,
TOK_RESERVED,
TOK_LIMIT
} JSTokenType;

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

bitwise-and (&) */
equality ops (== !=) */
relational ops (< <= > >=) */
shift ops (<< >> >>>) */
plus */
minus */
multiply/divide ops (* / %) */
unary prefix operator */
increment/decrement (++ --) */
member operator (.) */
identifier */
numeric constant */
string constant */
RegExp or other object constant */
true, false, null, this, super */
function keyword */
export keyword */
import keyword */
if keyword */
else keyword */
switch keyword */
case keyword */
default keyword */
while keyword */
do keyword */
for keyword */
break keyword */
continue keyword */
in keyword */
var keyword */
with keyword */
return keyword */
new keyword */
delete keyword */
#n= for object/array initializers */
#n# for object/array initializers */
try keyword */
catch keyword */
finally keyword */
throw keyword */
instanceof keyword */
debugger keyword */
reserved keywords */
domain size */

#define IS_PRIMARY_TOKEN(tt) \
((uintN)((tt) - TOK_NAME) <= (uintN)(TOK_PRIMARY - TOK_NAME))
struct JSTokenPtr {
uint16
uint16
};

index;
lineno;

/* index of char in physical line */


/* physical line number */

struct JSTokenPos {
JSTokenPtr
JSTokenPtr
};

begin;
end;

/* first character and line of token */


/* index 1 past last char, last line */

struct JSToken {

JSTokenType
JSTokenPos
jschar
union {
struct {
JSOp
JSAtom
} s;
jsdouble
} u;

type;
pos;
*ptr;

/* char value or above enumerator */


/* token position in file */
/* beginning of token in line buffer */

op;
*atom;

/* operator, for minimal parser */


/* atom table entry */

dval;

/* floating point number */

};
#define t_op
#define t_atom
#define t_dval

u.s.op
u.s.atom
u.dval

typedef struct JSTokenBuf {


jschar
*base;
jschar
*limit;
jschar
*ptr;
} JSTokenBuf;
#define JS_LINE_LIMIT

256

#define NTOKENS
#define NTOKENS_MASK

4
(NTOKENS-1)

/* base of line or stream buffer */


/* limit for quick bounds check */
/* next char to get, or slot to use */
/* logical line buffer size limit -physical line length is unlimited */
/* 1 current + 2 lookahead, rounded */
/* 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
#define TSF_EOF
#define TSF_NEWLINES
#define TSF_REGEXP
#define TSF_NLFLAG
#define TSF_CRFLAG
#define TSF_DIRTYLINE
rt of line */

*/
0x01
0x02
0x04
0x08
0x20
0x40
0x80

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

fatal error while compiling */


hit end of file */
tokenize newlines */
looking for a regular expression */
last linebuf ended with \n */
linebuf would have ended with \r */
stuff other than whitespace since sta

/*
* 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;
JSObject
*object;
JSScopeProperty *props;
JSScopeProperty **proptail;
#ifdef JS_DOUBLE_HASHING
uint32
tableLength;
JSScopeProperty *table;
uint32
gsTableLength;
JSPropertyOp
gsTable[1];
#else
JSScopeOps
*ops;
void
*data;
#endif
#ifdef JS_THREADSAFE
JSContext
*ownercx;
JSThinLock
lock;
union {
jsrefcount count;
JSScope
*link;
} u;
#ifdef DEBUG
const char
*file[4];
unsigned int
line[4];
#endif
#endif
};
#define OBJ_SCOPE(obj)
#define SPROP_GETTER(sprop,obj)
#define SPROP_SETTER(sprop,obj)

/*
/*
/*
/*

base class state */


object that owns this scope */
property list in definition order */
pointer to pointer to last prop */

/* number of entries in gsTable */


/* actually, gsTableLength ops */
/* virtual operations */
/* private data specific to ops */
/*
/*
/*
/*
/*

creating context, NULL if shared */


binary semaphore protecting scope */
union lockful and lock-free state: */
lock entry count for reentrancy */
next link in rt->scopeSharingTodo */

/* file where lock was (re-)taken */


/* line where lock was (re-)taken */

((JSScope *)(obj)->map)
SPROP_GETTER_SCOPE(sprop, OBJ_SCOPE(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;
uint8
attrs;
uint8
getterIndex;
uint8
setterIndex;
uint8
reserved;
JSScopeProperty *next;
};

/*
/*
/*
/*

index in obj->slots vector */


attributes, see jsapi.h JSPROP_ */
getter and setter method indexes */
in JSScope.gsTable[] */

/* 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
JSScope
JSSymbol
};

entry;
*scope;
*next;

#define sym_id(sym)
#define sym_atom(sym)
#define sym_property(sym)
struct JSScopeProperty {
jsrefcount
nrefs;
jsval
id;
JSPropertyOp
getter;
JSPropertyOp
setter;
uint32
slot;
uint8
attrs;
uint8
spare;
JSSymbol
*symbols;
JSScopeProperty *next;
JSScopeProperty **prevp;
};

/* base class state */


/* pointer to owning scope */
/* next in type-specific list */
((jsid)(sym)->entry.key)
((JSAtom *)(sym)->entry.key)
((JSScopeProperty *)(sym)->entry.value)
/* number of referencing symbols */
/* id passed to getter and setter */
/* getter and setter methods */
/*
/*
/*
/*
/*

index in obj->slots vector */


attributes, see jsapi.h JSPROP_ */
reserved for future use */
list of aliasing symbols */
doubly-linked list linkage */

#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,
#endif
{js_toString_str, script_toString,
{"compile",
script_compile,
{"exec",
script_exec,
#if JS_HAS_XDR
{"freeze",
script_freeze,
{js_thaw_str,
script_thaw,
#endif /* JS_HAS_XDR */
{0,0,0,0,0}
};

0,0,0},
0,0,0},
2,0,0},
1,0,0},
0,0,0},
1,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_EnumerateStub, JS_ResolveStub,
NULL,
NULL,
NULL,
NULL,
};

= {
JS_PropertyStub,
JS_ConvertStub,
script_call,
script_mark,

JS_PropertyStub,
script_finalize,
NULL,/*XXXbe xdr*/
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
uint32
jsbytecode

{
*code;
length;
*main;

/* bytecodes and their immediate operands */


/* length of code vector */
/* main entry point, after predef'ing prolog */

JSVersion
JSAtomMap
const char
uintN
uintN
jssrcnote
JSTryNote
JSPrincipals
JSObject

version;
atomMap;
*filename;
lineno;
depth;
*notes;
*trynotes;
*principals;
*object;

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

JS version under which script was compiled */


maps immediate index to literal struct */
source filename or null */
base line number of script */
maximum stack depth in slots */
line number and other decompiling data */
exception table for this script */
principals for this script */
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,
d>")
MSG_DEF(JSSMSG_CANT_OPEN,
)
MSG_DEF(JSSMSG_TRAP_USAGE,
c] expr")
MSG_DEF(JSSMSG_LINE2PC_USAGE,
line")
MSG_DEF(JSSMSG_FILE_SCRIPTS_ONLY,
ipts read from files")

0, 0, JSEXN_NONE, "<Error #0 is reserve


1, 2, JSEXN_NONE, "can't open {0}: {1}"
2, 0, JSEXN_NONE, "usage: trap [fun] [p
3, 0, JSEXN_NONE, "usage: line2pc [fun]
4, 0, JSEXN_NONE, "only works on JS scr

MSG_DEF(JSSMSG_UNEXPECTED_EOF,
}")
MSG_DEF(JSSMSG_DOEXP_USAGE,
)

5, 1, JSEXN_NONE, "unexpected EOF in {0


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
#define URL_XPALPHAS
#define URL_PATH
static
/*
*
*
*
*/
/*
{

((uint8) 1)
((uint8) 2)
((uint8) 4)

const uint8 urlCharType[256] =


Bit 0
xalpha
Bit 1
xpalpha
converts
Bit 2 ...
path

-- the alphas
-- as xalpha but
spaces to plus and plus to %20
-- 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,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,7,4,0,7,7,4,
7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7,
0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,
0, };

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

0x
1x
2x
3x
4x
5X
6x
7X

*/
*/
!"#$%&'()*+,-./
0123456789:;<=>?
@ABCDEFGHIJKLMNO
PQRSTUVWXYZ[\]^_
`abcdefghijklmno
pqrstuvwxyz{\}~

*/
*/
*/
*/
*/
DEL */

/* 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
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
} else {
newlength += 5; /* The character will be encoded as
}
}

need to be. */

'+' */
%XX */
%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;
GlobMode mode;
JSBool global;
JSString *str;
JSRegExp *regexp;
} GlobData;

{
/*
/*
/*
/*
/*

input: index of optional flags argument */


input: return index, match object, or void */
output: whether regexp was global */
output: 'this' parameter object as string */
output: regexp parameter object private data */

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;
JSObject
*lambda;
JSString
*repstr;
jschar
*dollar;
jschar
*chars;
size_t
length;
jsint
index;
jsint
leftIndex;
} ReplaceData;

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

base struct state */


replacement function object or null */
replacement string */
null or pointer to first $ in repstr */
result chars, null initially */
result length, 0 initially */
index in result of next replacement */
left context index in base.str->chars */

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_RegEx
pParenSubString
// (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 REGE
XP_PAREN_SUBSTRING with js_RegExpParenSubString
// (see comment from dgeorge i
n 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;
parlen = 0; /* Avoid warning. */
if (param) {
parlen = js_strlen(param);
taglen += 2 + parlen + 1;
}
endlen = strlen(end);
taglen += str->length + 2 + endlen + 1;

/* '<begin' + '>' */

/* '="param"' */
/* '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,
{js_toSource_str,
str_toSource,
#endif
/* Java-like methods. */
{js_toString_str,
str_toString,
{js_valueOf_str,
str_valueOf,
{"substring",
str_substring,
{"toLowerCase",
str_toLowerCase,
{"toUpperCase",
str_toUpperCase,
{"charAt",
str_charAt,
{"charCodeAt",
str_charCodeAt,
{"indexOf",
str_indexOf,
{"lastIndexOf",
str_lastIndexOf,
{"toLocaleLowerCase", str_toLocaleLowerCase,
{"toLocaleUpperCase", str_toLocaleUpperCase,
{"localeCompare",
str_localeCompare,

0,0,0},
0,0,0},

0,0,0},
0,0,0},
2,0,0},
0,0,0},
0,0,0},
1,0,0},
1,0,0},
2,0,0},
2,0,0},
0,0,0},
0,0,0},
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,
{"slice",
str_slice,
#endif
/* HTML string methods. */
{"bold",
str_bold,
{"italics",
str_italics,
{"fixed",
str_fixed,
{"fontsize",
str_fontsize,
{"fontcolor",
str_fontcolor,
{"link",
str_link,
{"anchor",
str_anchor,
{"strike",
str_strike,
{"small",
str_small,
{"big",
str_big,
{"blink",
str_blink,
{"sup",
str_sup,
{"sub",
str_sub,
{0,0,0,0,0}

0,0,0},
0,0,0},

0,0,0},
0,0,0},
0,0,0},
1,0,0},
1,0,0},
1,0,0},
1,0,0},
0,0,0},
0,0,0},
0,0,0},
0,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
static
#ifdef
static
#endif

JSHashTable *deflated_string_cache;
uint32 deflated_string_cache_bytes;
JS_THREADSAFE
JSLock *deflated_string_cache_lock;

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
1 bit
1 bit
1 bit
3 bits

2 bits

5 bits

signed offset used for converting case


if 1, adding the signed offset converts the character to
lowercase
if 1, subtracting the signed offset converts the character to
uppercase
if 1, character has a titlecase equivalent (possibly itself)
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
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
digit offset

* 4 bits
* 5 bits
*/

reserved for future use


character type

/* The X table has 1024 entries for a total of 1024 bytes. */


const
0,
8,
16,
24,
28,
33,
41,
47,
28,
28,
28,
28,
28,
28,
28,
60,
67,
75,
79,
86,
28,
28,
28,
28,
90,
96,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,

uint8 js_X[] =
1, 2, 3,
9, 10, 11,
17, 18, 19,
25, 26, 27,
28, 28, 28,
34, 35, 36,
42, 43, 44,
48, 49, 50,
28, 54, 55,
28, 28, 28,
28, 28, 28,
28, 28, 28,
28, 28, 28,
28, 28, 28,
28, 28, 28,
60, 61, 62,
68, 69, 70,
75, 75, 76,
80, 81, 82,
85, 28, 28,
28, 28, 28,
28, 28, 28,
28, 28, 28,
28, 28, 28,
91, 92, 93,
97, 98, 99,
28, 28, 28,
28, 28, 28,
28, 28, 28,
28, 28, 28,
28, 28, 28,
28, 28, 28,
28, 28, 28,
28, 28, 28,
28, 28, 28,
28, 28, 28,
28, 28, 28,
28, 28, 28,
28, 28, 28,
56, 56, 56,
56, 56, 56,
56, 56, 56,
56, 56, 56,
56, 56, 56,
56, 56, 56,
56, 56, 56,
56, 56, 56,
56, 56, 56,
56, 56, 56,
56, 56, 56,
56, 56, 56,
56, 56, 56,
56, 56, 56,

{
4,
12,
20,
28,
29,
37,
45,
51,
56,
28,
28,
28,
28,
28,
28,
63,
71,
77,
83,
87,
28,
28,
28,
28,
94,
83,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,

5,
13,
21,
28,
30,
38,
46,
52,
57,
28,
28,
28,
28,
28,
28,
64,
72,
78,
83,
88,
28,
28,
28,
28,
56,
100,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,

6,
14,
22,
28,
31,
39,
28,
53,
58,
28,
28,
28,
28,
28,
28,
65,
73,
28,
84,
89,
28,
28,
28,
28,
95,
83,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,

7,
15,
23,
28,
32,
40,
28,
28,
59,
28,
28,
28,
28,
28,
28,
66,
74,
28,
85,
28,
28,
28,
28,
28,
28,
101,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
28,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,

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

0x0000
0x0200
0x0400
0x0600
0x0800
0x0A00
0x0C00
0x0E00
0x1000
0x1200
0x1400
0x1600
0x1800
0x1A00
0x1C00
0x1E00
0x2000
0x2200
0x2400
0x2600
0x2800
0x2A00
0x2C00
0x2E00
0x3000
0x3200
0x3400
0x3600
0x3800
0x3A00
0x3C00
0x3E00
0x4000
0x4200
0x4400
0x4600
0x4800
0x4A00
0x4C00
0x4E00
0x5000
0x5200
0x5400
0x5600
0x5800
0x5A00
0x5C00
0x5E00
0x6000
0x6200
0x6400
0x6600
0x6800

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

56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
28,
28,
28,
28,
28,
28,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
104,
104,
104,
104,
105,

56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
28,
28,
28,
28,
28,
28,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
104,
104,
104,
104,
105,

56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
28,
28,
28,
28,
28,
28,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
104,
104,
104,
104,
105,

56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
28,
28,
28,
28,
28,
28,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
104,
104,
104,
104,
105,

56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
28,
28,
28,
28,
28,
28,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
104,
104,
104,
104,
105,

56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
28,
28,
28,
28,
28,
28,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
104,
104,
104,
104,
105,

56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
102,
28,
28,
28,
28,
28,
28,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
103,
104,
104,
104,
104,
105,

56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
28,
28,
28,
28,
28,
28,
28,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
56,
28,
104,
104,
104,
104,
105,

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

0x6A00
0x6C00
0x6E00
0x7000
0x7200
0x7400
0x7600
0x7800
0x7A00
0x7C00
0x7E00
0x8000
0x8200
0x8400
0x8600
0x8800
0x8A00
0x8C00
0x8E00
0x9000
0x9200
0x9400
0x9600
0x9800
0x9A00
0x9C00
0x9E00
0xA000
0xA200
0xA400
0xA600
0xA800
0xAA00
0xAC00
0xAE00
0xB000
0xB200
0xB400
0xB600
0xB800
0xBA00
0xBC00
0xBE00
0xC000
0xC200
0xC400
0xC600
0xC800
0xCA00
0xCC00
0xCE00
0xD000
0xD200
0xD400
0xD600
0xD800
0xDA00
0xDC00
0xDE00
0xE000

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

105,
105,
105,
105,
105,
105,
105,
105,
105,
105,
105,
105,
106,
56,
115,
};

105,
105,
105,
105,
105,
105,
105,
105,
105,
105,
105,
105,
28,
56,
116,

105,
105,
105,
105,
105,
105,
105,
105,
105,
105,
105,
105,
28,
56,
56,

105,
105,
105,
105,
105,
105,
105,
105,
105,
105,
105,
105,
28,
56,
117,

105,
105,
105,
105,
105,
105,
105,
105,
105,
105,
105,
56,
107,
111,
118,

105,
105,
105,
105,
105,
105,
105,
105,
105,
105,
105,
56,
108,
112,
119,

105,
105,
105,
105,
105,
105,
105,
105,
105,
105,
105,
56,
109,
113,
120,

105,
105,
105,
105,
105,
105,
105,
105,
105,
105,
105,
56,
110,
114,
121

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

0xE200
0xE400
0xE600
0xE800
0xEA00
0xEC00
0xEE00
0xF000
0xF200
0xF400
0xF600
0xF800
0xFA00
0xFC00
0xFE00

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

/* The Y table has 7808 entries for a total of 7808 bytes. */


const
0,
0,
0,
0,
2,
5,
9,
9,
3,
10,
10,
10,
11,
13,
13,
13,
0,
0,
0,
0,
2,
11,
15,
11,
20,
20,
20,
20,
21,
21,
21,
21,
23,
23,
23,
23,
23,
23,
25,
16,

uint8 js_Y[] =
0, 0, 0,
1, 1, 1,
0, 0, 0,
0, 0, 0,
3, 3, 3,
6, 3, 7,
9, 9, 9,
9, 3, 3,
10, 10, 10,
10, 10, 10,
10, 10, 10,
10, 10, 5,
13, 13, 13,
13, 13, 13,
13, 13, 13,
13, 13, 5,
0, 0, 0,
0, 0, 0,
0, 0, 0,
0, 0, 0,
3, 4, 4,
15, 16, 5,
7, 17, 17,
18, 16, 6,
20, 20, 20,
20, 20, 20,
20, 20, 20,
20, 20, 20,
21, 21, 21,
21, 21, 21,
21, 21, 21,
21, 21, 21,
24, 23, 24,
24, 23, 24,
24, 23, 24,
24, 23, 24,
24, 23, 24,
24, 23, 24,
26, 23, 24,
23, 24, 23,

{
0,
1,
0,
0,
4,
3,
9,
7,
10,
10,
10,
3,
13,
13,
13,
7,
0,
0,
0,
0,
4,
7,
11,
19,
20,
20,
20,
20,
21,
21,
21,
21,
23,
23,
23,
23,
23,
23,
23,
24,

0,
1,
0,
0,
3,
8,
9,
7,
10,
10,
10,
6,
13,
13,
13,
6,
0,
0,
0,
0,
4,
8,
16,
19,
20,
20,
20,
20,
21,
21,
21,
21,
24,
24,
24,
24,
24,
24,
24,
23,

0,
0,
0,
0,
3,
3,
9,
7,
10,
10,
10,
11,
13,
13,
13,
7,
0,
0,
0,
0,
15,
15,
15,
19,
20,
20,
20,
20,
21,
21,
21,
21,
23,
23,
23,
23,
23,
23,
23,
24,

0,
0,
0,
0,
3,
3,
9,
3,
10,
10,
10,
12,
13,
13,
13,
0,
0,
0,
0,
0,
15,
11,
3,
3,
20,
20,
7,
16,
21,
21,
7,
22,
24,
24,
24,
24,
24,
24,
24,
23,

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

0
0
0
0
0
0
0
0
1
1
1
1
1
1
1
1
2
2
2
2
2
2
2
2
3
3
3
3
3
3
3
3
4
4
4
4
4
4
4
4

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

24,
24,
23,
23,
23,
23,
23,
27,
16,
24,
33,
23,
23,
24,
24,
23,
40,
44,
24,
24,
23,
23,
16,
46,
23,
23,
23,
46,
46,
46,
46,
46,
46,
46,
16,
50,
49,
53,
16,
16,
16,
56,
16,
16,
16,
16,
59,
59,
59,
11,
59,
11,
59,
11,
46,
46,
60,
60,
60,
60,

23,
16,
24,
24,
24,
24,
24,
23,
29,
31,
23,
24,
24,
39,
41,
24,
40,
45,
23,
23,
24,
24,
43,
46,
24,
24,
24,
46,
46,
46,
46,
46,
46,
46,
16,
50,
16,
54,
16,
16,
16,
16,
16,
16,
16,
46,
59,
11,
59,
11,
59,
11,
59,
11,
46,
46,
60,
60,
60,
60,

24,
23,
23,
23,
23,
23,
23,
24,
23,
31,
24,
16,
23,
40,
41,
16,
40,
43,
24,
24,
23,
23,
44,
23,
23,
23,
23,
46,
46,
46,
46,
46,
46,
46,
16,
16,
16,
16,
55,
16,
16,
57,
58,
16,
16,
46,
59,
11,
11,
11,
11,
11,
59,
46,
46,
46,
60,
60,
60,
60,

23,
24,
24,
24,
24,
24,
24,
23,
24,
23,
31,
16,
24,
16,
23,
40,
40,
44,
23,
23,
24,
24,
45,
24,
24,
24,
24,
46,
46,
46,
46,
46,
46,
46,
47,
51,
52,
16,
16,
16,
56,
57,
16,
16,
16,
46,
59,
59,
11,
11,
11,
11,
59,
46,
46,
46,
60,
60,
60,
60,

24,
23,
23,
23,
23,
23,
23,
24,
23,
24,
34,
35,
23,
23,
24,
23,
43,
45,
24,
24,
23,
23,
23,
23,
23,
23,
23,
46,
46,
46,
46,
46,
46,
46,
48,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
46,
59,
59,
11,
11,
11,
11,
59,
46,
46,
46,
60,
60,
60,
60,

23,
24,
24,
24,
24,
24,
24,
23,
24,
16,
16,
37,
24,
24,
23,
24,
44,
23,
23,
16,
24,
24,
24,
24,
24,
24,
24,
46,
46,
46,
46,
46,
46,
46,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
46,
59,
59,
11,
11,
11,
11,
11,
46,
46,
46,
60,
60,
60,
60,

24,
23,
23,
23,
23,
23,
23,
24,
30,
32,
35,
16,
38,
39,
24,
40,
45,
24,
24,
23,
23,
23,
46,
23,
23,
23,
23,
46,
46,
46,
46,
46,
46,
46,
49,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
46,
59,
59,
11,
11,
11,
11,
11,
46,
46,
46,
60,
60,
60,
60,

23,
24,
24,
24,
24,
24,
24,
28,
23,
32,
36,
38,
23,
23,
42,
40,
43,
23,
23,
24,
24,
24,
46,
24,
24,
24,
24,
46,
46,
46,
46,
46,
46,
46,
49,
16,
16,
54,
16,
16,
16,
16,
16,
16,
16,
46,
59,
59,
11,
11,
11,
46,
11,
46,
46,
46,
60,
60,
60,
60,

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

5
5
5
5
5
5
5
5
6
6
6
6
6
6
6
6
7
7
7
7
7
7
7
7
8
8
8
8
8
8
8
8
9
9
9
9
9
9
9
9
10
10
10
10
10
10
10
10
11
11
11
11
11
11
11
11
12
12
12
12

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

60,
60,
60,
60,
60,
46,
46,
46,
60,
46,
46,
46,
46,
62,
16,
20,
20,
20,
16,
21,
21,
21,
69,
46,
38,
23,
73,
46,
46,
75,
20,
20,
20,
20,
21,
21,
21,
21,
46,
74,
23,
23,
23,
23,
23,
46,
23,
23,
23,
23,
23,
23,
40,
24,
23,
23,
23,
23,
23,
23,

60,
60,
60,
60,
60,
46,
46,
46,
60,
46,
46,
46,
46,
62,
20,
20,
20,
20,
21,
21,
21,
21,
70,
46,
46,
24,
74,
46,
75,
75,
20,
20,
20,
20,
21,
21,
21,
21,
74,
74,
24,
24,
24,
24,
24,
46,
24,
24,
24,
24,
24,
24,
23,
46,
24,
24,
24,
24,
24,
24,

60,
60,
60,
60,
60,
46,
46,
46,
46,
46,
46,
59,
46,
62,
20,
20,
46,
20,
21,
21,
16,
21,
38,
38,
23,
23,
16,
46,
75,
75,
20,
20,
20,
20,
21,
21,
21,
21,
74,
74,
23,
23,
23,
23,
15,
46,
23,
23,
23,
23,
23,
23,
24,
46,
23,
23,
23,
23,
23,
46,

60,
60,
60,
60,
60,
46,
46,
46,
46,
46,
46,
46,
46,
46,
20,
20,
20,
20,
21,
21,
21,
21,
38,
46,
24,
24,
40,
46,
75,
75,
20,
20,
20,
20,
21,
21,
21,
21,
74,
74,
24,
24,
24,
24,
60,
46,
24,
24,
24,
24,
24,
24,
23,
23,
24,
24,
24,
24,
24,
46,

60,
60,
60,
60,
60,
46,
46,
46,
46,
46,
3,
46,
11,
63,
20,
20,
20,
65,
21,
21,
21,
67,
38,
38,
23,
23,
46,
46,
75,
75,
20,
20,
20,
20,
21,
21,
21,
21,
74,
74,
23,
23,
23,
23,
60,
46,
23,
23,
23,
23,
23,
23,
24,
24,
23,
23,
23,
46,
23,
46,

60,
60,
60,
60,
60,
46,
46,
46,
46,
46,
3,
46,
11,
46,
20,
20,
20,
66,
21,
21,
21,
68,
71,
46,
24,
24,
46,
46,
75,
46,
20,
20,
20,
20,
21,
21,
21,
21,
74,
46,
24,
24,
24,
24,
60,
46,
24,
24,
24,
24,
24,
24,
46,
46,
24,
24,
24,
46,
24,
46,

60,
60,
60,
60,
46,
46,
46,
46,
46,
46,
46,
3,
61,
64,
20,
20,
20,
66,
21,
21,
21,
68,
72,
38,
23,
23,
46,
46,
75,
75,
20,
20,
20,
20,
21,
21,
21,
21,
74,
74,
23,
23,
23,
23,
60,
46,
23,
23,
23,
23,
23,
23,
46,
46,
23,
23,
23,
23,
46,
46,

60,
60,
60,
60,
46,
46,
46,
46,
46,
46,
46,
46,
3,
64,
20,
20,
20,
66,
21,
21,
21,
46,
46,
46,
24,
24,
46,
46,
75,
75,
20,
20,
20,
20,
21,
21,
21,
21,
74,
74,
24,
24,
24,
24,
46,
46,
24,
24,
24,
24,
24,
24,
23,
46,
24,
24,
24,
24,
46,
46,

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

12
12
12
12
13
13
13
13
13
13
13
13
14
14
14
14
14
14
14
14
15
15
15
15
15
15
15
15
16
16
16
16
16
16
16
16
17
17
17
17
17
17
17
17
18
18
18
18
18
18
18
18
19
19
19
19
19
19
19
19

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

46,
46,
46,
46,
46,
46,
46,
76,
76,
76,
76,
46,
46,
77,
77,
77,
77,
46,
46,
60,
60,
60,
60,
60,
3,
46,
40,
40,
40,
40,
40,
46,
46,
46,
46,
46,
46,
40,
40,
40,
59,
40,
60,
46,
78,
78,
60,
40,
40,
40,
40,
40,
40,
40,
40,
46,
40,
40,
40,
60,

46,
46,
46,
46,
46,
46,
76,
76,
76,
76,
76,
59,
77,
77,
77,
77,
77,
3,
60,
60,
60,
60,
60,
60,
60,
46,
40,
40,
40,
40,
40,
46,
46,
46,
46,
46,
40,
40,
40,
40,
40,
40,
60,
46,
78,
78,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
40,
40,
40,
60,

46,
46,
46,
46,
46,
46,
76,
76,
76,
76,
76,
3,
77,
77,
77,
77,
77,
46,
60,
60,
46,
60,
60,
46,
60,
46,
40,
40,
40,
40,
40,
46,
46,
46,
46,
46,
40,
40,
40,
40,
40,
40,
60,
46,
78,
3,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
60,

46,
46,
46,
46,
46,
46,
76,
76,
76,
76,
76,
3,
77,
77,
77,
77,
77,
46,
60,
60,
60,
60,
60,
60,
3,
46,
40,
40,
40,
46,
3,
46,
46,
46,
46,
3,
40,
40,
40,
46,
40,
60,
46,
46,
78,
3,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
60,

46,
46,
46,
46,
46,
46,
76,
76,
76,
76,
76,
3,
77,
77,
77,
77,
77,
46,
60,
60,
60,
60,
60,
60,
60,
46,
40,
40,
40,
46,
3,
46,
46,
3,
46,
46,
40,
40,
40,
46,
40,
60,
46,
46,
78,
3,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
3,
60,

46,
46,
46,
46,
46,
46,
76,
76,
76,
76,
76,
3,
77,
77,
77,
77,
77,
46,
60,
60,
60,
60,
60,
60,
46,
46,
40,
40,
40,
46,
46,
46,
46,
46,
46,
46,
40,
40,
40,
46,
40,
60,
46,
46,
78,
3,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
79,

46,
46,
46,
46,
46,
46,
76,
76,
76,
76,
76,
3,
77,
77,
77,
77,
77,
46,
60,
60,
60,
60,
60,
3,
46,
46,
40,
40,
40,
46,
46,
46,
46,
46,
46,
46,
40,
40,
40,
46,
40,
60,
46,
46,
78,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
60,
79,

46,
46,
46,
46,
46,
46,
76,
76,
76,
76,
46,
3,
77,
77,
77,
77,
16,
46,
60,
60,
60,
60,
60,
60,
46,
46,
40,
40,
40,
46,
46,
46,
46,
46,
46,
3,
40,
40,
40,
46,
40,
60,
46,
46,
78,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
40,
46,
60,
60,

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

20
20
20
20
20
20
20
20
21
21
21
21
21
21
21
21
22
22
22
22
22
22
22
22
23
23
23
23
23
23
23
23
24
24
24
24
24
24
24
24
25
25
25
25
25
25
25
25
26
26
26
26
26
26
26
26
27
27
27
27

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

60,
60,
9,
9,
46,
46,
46,
46,
46,
46,
46,
46,
46,
40,
40,
40,
40,
40,
40,
40,
80,
60,
15,
40,
40,
81,
3,
46,
46,
40,
40,
40,
40,
40,
40,
40,
80,
80,
46,
46,
40,
81,
40,
19,
46,
40,
40,
40,
40,
40,
40,
40,
80,
60,
46,
46,
46,
81,
60,
46,

60,
15,
9,
9,
46,
46,
46,
46,
46,
46,
46,
46,
60,
40,
40,
40,
40,
40,
40,
40,
60,
80,
60,
40,
40,
81,
46,
46,
60,
40,
46,
40,
40,
46,
46,
40,
60,
46,
46,
46,
40,
81,
40,
83,
46,
40,
46,
40,
40,
46,
46,
40,
60,
46,
46,
40,
46,
81,
60,
46,

60,
60,
9,
46,
46,
46,
46,
46,
46,
46,
46,
46,
60,
40,
40,
40,
40,
40,
40,
46,
60,
80,
60,
40,
60,
81,
46,
46,
80,
40,
46,
40,
40,
40,
40,
46,
60,
46,
46,
46,
60,
81,
4,
15,
60,
40,
46,
40,
40,
40,
40,
46,
60,
46,
46,
40,
46,
81,
40,
46,

60,
60,
9,
46,
46,
46,
46,
46,
46,
46,
46,
46,
80,
40,
40,
40,
40,
40,
40,
46,
60,
80,
60,
40,
60,
81,
46,
46,
80,
40,
40,
40,
40,
40,
46,
46,
60,
80,
46,
46,
60,
81,
4,
46,
46,
46,
40,
40,
40,
40,
40,
46,
46,
60,
46,
40,
46,
81,
40,
46,

60,
60,
9,
46,
46,
46,
46,
46,
46,
46,
46,
46,
46,
40,
40,
40,
40,
40,
40,
60,
60,
80,
60,
40,
3,
81,
46,
46,
46,
40,
40,
40,
40,
40,
46,
60,
60,
80,
46,
40,
46,
81,
82,
46,
46,
46,
40,
40,
40,
40,
46,
60,
46,
60,
46,
40,
46,
81,
40,
46,

59,
60,
9,
46,
46,
46,
46,
46,
46,
46,
46,
46,
40,
40,
40,
40,
40,
40,
40,
40,
60,
60,
46,
40,
3,
81,
46,
46,
40,
46,
40,
40,
40,
40,
46,
46,
46,
60,
46,
40,
46,
81,
82,
46,
40,
46,
40,
40,
40,
40,
40,
46,
46,
60,
46,
46,
46,
81,
46,
46,

59,
46,
9,
46,
46,
46,
46,
46,
46,
46,
46,
46,
40,
40,
40,
40,
40,
40,
40,
80,
60,
46,
46,
40,
81,
81,
46,
46,
40,
46,
40,
40,
40,
40,
40,
80,
46,
46,
46,
46,
81,
81,
82,
46,
40,
46,
40,
40,
40,
40,
40,
80,
46,
46,
46,
40,
81,
81,
46,
46,

60,
46,
9,
46,
46,
46,
46,
46,
46,
46,
46,
46,
40,
40,
40,
40,
40,
40,
40,
80,
60,
46,
46,
40,
81,
81,
46,
46,
40,
40,
40,
40,
40,
40,
40,
80,
80,
46,
80,
40,
81,
81,
82,
46,
40,
40,
40,
40,
40,
40,
46,
80,
60,
46,
46,
46,
81,
81,
46,
46,

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

27
27
27
27
28
28
28
28
28
28
28
28
29
29
29
29
29
29
29
29
30
30
30
30
30
30
30
30
31
31
31
31
31
31
31
31
32
32
32
32
32
32
32
32
33
33
33
33
33
33
33
33
34
34
34
34
34
34
34
34

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

46,
40,
40,
40,
40,
40,
40,
40,
80,
60,
15,
46,
40,
81,
46,
46,
46,
40,
40,
40,
40,
40,
40,
40,
80,
80,
46,
46,
40,
81,
15,
46,
46,
40,
40,
46,
46,
40,
40,
40,
60,
80,
46,
46,
46,
81,
84,
46,
46,
40,
40,
40,
40,
40,
40,
40,
60,
60,
46,
46,

60,
40,
40,
40,
40,
46,
46,
40,
60,
80,
46,
46,
46,
81,
46,
46,
60,
40,
46,
40,
40,
46,
46,
40,
60,
46,
46,
46,
40,
81,
46,
46,
46,
40,
46,
40,
46,
40,
40,
40,
80,
46,
46,
46,
46,
81,
19,
46,
80,
40,
46,
40,
40,
46,
40,
40,
80,
46,
46,
46,

60,
40,
46,
40,
40,
40,
40,
46,
60,
46,
46,
46,
46,
81,
46,
46,
80,
40,
46,
40,
40,
40,
40,
46,
60,
46,
46,
46,
46,
81,
46,
46,
60,
40,
40,
40,
46,
40,
40,
46,
80,
80,
46,
46,
46,
81,
19,
46,
80,
40,
40,
40,
40,
40,
40,
46,
80,
60,
46,
46,

80,
40,
40,
40,
40,
40,
40,
46,
60,
80,
46,
46,
46,
81,
46,
46,
80,
40,
40,
40,
40,
40,
40,
46,
60,
80,
46,
46,
46,
81,
46,
46,
80,
46,
40,
46,
40,
46,
40,
46,
46,
80,
46,
46,
46,
81,
46,
46,
80,
40,
40,
40,
40,
40,
40,
46,
80,
60,
46,
46,

46,
46,
40,
40,
40,
40,
46,
60,
60,
80,
46,
46,
46,
81,
46,
46,
46,
40,
40,
40,
40,
40,
46,
60,
46,
80,
46,
40,
46,
81,
46,
46,
46,
46,
40,
40,
40,
46,
40,
46,
46,
80,
46,
46,
46,
81,
46,
46,
46,
40,
40,
40,
40,
40,
46,
46,
80,
60,
46,
46,

40,
40,
40,
40,
40,
40,
40,
40,
60,
60,
46,
46,
46,
81,
46,
46,
40,
46,
40,
40,
40,
40,
46,
40,
46,
60,
46,
40,
46,
81,
46,
46,
40,
46,
40,
46,
46,
46,
40,
46,
46,
60,
46,
46,
46,
81,
46,
46,
40,
46,
40,
40,
40,
40,
40,
46,
46,
60,
60,
46,

40,
46,
40,
40,
40,
40,
40,
80,
46,
46,
46,
46,
81,
81,
46,
46,
40,
46,
40,
40,
40,
40,
40,
80,
46,
46,
60,
46,
81,
81,
46,
46,
40,
40,
46,
40,
46,
40,
46,
80,
80,
46,
46,
46,
46,
81,
46,
46,
40,
40,
40,
40,
40,
40,
40,
60,
60,
46,
60,
46,

40,
40,
40,
40,
40,
40,
40,
80,
60,
46,
46,
46,
81,
81,
46,
46,
40,
40,
40,
40,
40,
40,
40,
60,
80,
46,
80,
40,
81,
81,
46,
46,
40,
40,
46,
40,
46,
40,
40,
80,
80,
46,
80,
46,
81,
81,
46,
46,
40,
40,
40,
40,
40,
40,
40,
60,
60,
46,
46,
46,

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

35
35
35
35
35
35
35
35
36
36
36
36
36
36
36
36
37
37
37
37
37
37
37
37
38
38
38
38
38
38
38
38
39
39
39
39
39
39
39
39
40
40
40
40
40
40
40
40
41
41
41
41
41
41
41
41
42
42
42
42

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

40,
81,
46,
46,
46,
40,
40,
40,
40,
40,
40,
40,
80,
80,
46,
46,
40,
81,
46,
46,
46,
40,
40,
40,
40,
40,
40,
40,
80,
80,
46,
46,
40,
81,
46,
46,
46,
40,
40,
40,
40,
40,
40,
60,
40,
60,
9,
9,
46,
46,
46,
46,
46,
40,
46,
46,
46,
46,
40,
60,

40,
81,
46,
46,
46,
40,
46,
40,
40,
46,
40,
40,
80,
46,
46,
46,
40,
81,
46,
46,
46,
40,
46,
40,
40,
46,
40,
40,
60,
46,
46,
46,
40,
81,
46,
46,
40,
40,
40,
40,
40,
40,
60,
60,
40,
60,
9,
9,
46,
46,
46,
46,
40,
46,
46,
40,
40,
46,
60,
60,

46,
81,
46,
46,
80,
40,
40,
40,
40,
40,
40,
46,
80,
80,
46,
46,
46,
81,
46,
46,
80,
40,
40,
40,
40,
40,
40,
46,
60,
80,
46,
46,
46,
81,
46,
46,
40,
40,
40,
40,
40,
40,
40,
60,
40,
60,
9,
3,
46,
46,
46,
46,
40,
40,
46,
40,
40,
40,
40,
46,

46,
81,
46,
46,
80,
40,
40,
40,
40,
40,
40,
46,
80,
80,
46,
46,
46,
81,
46,
46,
80,
40,
40,
40,
40,
40,
40,
46,
60,
80,
46,
46,
46,
81,
46,
46,
40,
40,
40,
40,
40,
40,
40,
46,
40,
60,
9,
3,
46,
46,
46,
46,
46,
46,
46,
40,
40,
40,
40,
60,

46,
81,
46,
46,
46,
40,
40,
40,
40,
40,
46,
46,
80,
60,
46,
46,
46,
81,
46,
46,
46,
40,
40,
40,
40,
40,
40,
46,
46,
80,
46,
46,
46,
81,
46,
46,
40,
40,
40,
40,
40,
40,
60,
46,
40,
60,
9,
46,
46,
46,
46,
46,
40,
46,
40,
40,
46,
46,
60,
60,

46,
81,
46,
46,
40,
46,
40,
40,
40,
40,
40,
46,
46,
60,
80,
46,
46,
81,
46,
46,
40,
46,
40,
40,
40,
40,
40,
46,
46,
60,
46,
46,
46,
81,
46,
46,
40,
40,
40,
40,
40,
40,
60,
46,
40,
60,
9,
46,
46,
46,
46,
46,
46,
40,
40,
40,
40,
40,
60,
40,

81,
81,
46,
46,
40,
40,
40,
40,
40,
40,
40,
80,
60,
46,
80,
40,
81,
81,
46,
46,
40,
40,
40,
40,
40,
40,
40,
80,
80,
46,
46,
46,
81,
81,
46,
46,
40,
40,
40,
40,
40,
40,
60,
46,
59,
60,
9,
46,
46,
46,
46,
46,
46,
46,
40,
40,
46,
40,
60,
46,

81,
81,
46,
46,
40,
40,
40,
40,
40,
40,
40,
60,
80,
46,
46,
46,
81,
81,
46,
46,
40,
40,
40,
40,
40,
40,
40,
80,
80,
46,
80,
46,
81,
81,
46,
46,
40,
40,
40,
40,
40,
3,
60,
4,
60,
15,
9,
46,
46,
46,
46,
46,
40,
46,
40,
40,
40,
3,
60,
46,

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

42
42
42
42
43
43
43
43
43
43
43
43
44
44
44
44
44
44
44
44
45
45
45
45
45
45
45
45
46
46
46
46
46
46
46
46
47
47
47
47
47
47
47
47
48
48
48
48
48
48
48
48
49
49
49
49
49
49
49
49

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

40,
60,
9,
9,
46,
46,
46,
46,
15,
3,
3,
60,
78,
78,
85,
15,
40,
46,
40,
40,
40,
40,
46,
60,
60,
60,
60,
46,
60,
60,
46,
46,
46,
46,
46,
46,
76,
76,
76,
76,
76,
46,
16,
16,
16,
16,
16,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,

40,
60,
9,
9,
46,
46,
46,
46,
15,
3,
3,
60,
78,
78,
85,
60,
40,
40,
40,
40,
40,
40,
60,
60,
60,
60,
60,
60,
60,
60,
60,
60,
46,
46,
46,
46,
76,
76,
76,
76,
76,
46,
16,
16,
16,
16,
16,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,

40,
60,
9,
46,
46,
46,
46,
46,
15,
3,
3,
15,
78,
85,
85,
5,
40,
40,
40,
40,
40,
46,
60,
60,
60,
60,
60,
60,
60,
60,
60,
46,
46,
46,
46,
46,
76,
76,
76,
76,
76,
46,
16,
16,
16,
16,
16,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,

40,
60,
9,
46,
46,
46,
46,
46,
15,
3,
15,
15,
78,
85,
85,
6,
40,
40,
40,
40,
40,
46,
60,
60,
60,
60,
60,
60,
60,
60,
60,
46,
46,
46,
46,
46,
76,
76,
76,
76,
76,
46,
16,
16,
16,
16,
16,
3,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,

40,
60,
9,
40,
46,
46,
46,
46,
3,
3,
15,
15,
78,
85,
15,
5,
40,
40,
40,
40,
40,
46,
60,
60,
60,
46,
60,
60,
60,
60,
60,
46,
46,
46,
46,
46,
76,
76,
76,
76,
76,
46,
16,
16,
16,
16,
16,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,

46,
60,
9,
40,
46,
46,
46,
46,
3,
3,
15,
15,
78,
85,
60,
6,
40,
40,
40,
40,
40,
46,
60,
60,
3,
46,
60,
60,
60,
60,
60,
46,
46,
46,
46,
46,
76,
76,
76,
76,
76,
46,
16,
16,
16,
16,
16,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,

59,
46,
9,
46,
46,
46,
46,
46,
3,
3,
15,
15,
78,
85,
15,
80,
40,
40,
40,
40,
40,
46,
60,
60,
60,
46,
46,
60,
60,
46,
60,
46,
46,
46,
46,
46,
76,
76,
76,
76,
46,
46,
16,
16,
16,
16,
16,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,

46,
46,
9,
46,
46,
46,
46,
46,
3,
3,
15,
15,
78,
85,
60,
80,
40,
40,
40,
40,
40,
46,
60,
80,
60,
46,
60,
60,
60,
46,
60,
46,
46,
46,
46,
46,
76,
76,
76,
76,
46,
46,
16,
16,
16,
16,
46,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,

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

50
50
50
50
50
50
50
50
51
51
51
51
51
51
51
51
52
52
52
52
52
52
52
52
53
53
53
53
53
53
53
53
54
54
54
54
54
54
54
54
55
55
55
55
55
55
55
55
56
56
56
56
56
56
56
56
57
57
57
57

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

40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
23,
23,
23,
23,
23,
23,
23,
23,
23,
23,
23,
16,
23,
23,
23,
23,
23,
23,
23,
23,
23,
23,
23,
23,
86,
87,
86,
87,
86,
87,
86,
87,
86,
87,
16,
46,
86,
87,
88,
91,

40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
16,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
86,
87,
86,
87,
86,
87,
86,
87,
86,
87,
86,
87,
86,
87,
88,
91,

40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
23,
23,
23,
23,
23,
23,
23,
23,
23,
23,
23,
16,
23,
23,
23,
23,
23,
23,
23,
23,
23,
23,
23,
46,
86,
87,
86,
87,
86,
87,
86,
87,
86,
87,
16,
46,
86,
87,
89,
92,

40,
40,
40,
40,
40,
40,
40,
40,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
16,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
46,
86,
87,
86,
87,
86,
87,
86,
87,
86,
87,
86,
87,
86,
87,
89,
92,

40,
40,
40,
40,
40,
40,
40,
40,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
23,
23,
23,
23,
23,
23,
23,
23,
23,
23,
23,
46,
23,
23,
23,
23,
23,
23,
23,
23,
23,
23,
23,
46,
86,
87,
86,
87,
86,
87,
86,
87,
86,
87,
16,
46,
86,
87,
89,
93,

40,
40,
40,
40,
40,
40,
40,
40,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
46,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
46,
86,
87,
86,
87,
86,
87,
86,
87,
86,
87,
86,
87,
86,
87,
89,
93,

40,
40,
40,
40,
40,
40,
40,
40,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
23,
23,
23,
23,
23,
23,
23,
23,
23,
23,
16,
46,
23,
23,
23,
23,
23,
23,
23,
23,
23,
23,
23,
46,
86,
87,
46,
46,
86,
87,
86,
87,
46,
46,
16,
46,
86,
87,
90,
46,

40,
40,
40,
40,
40,
40,
40,
40,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
16,
46,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
24,
46,
86,
87,
46,
46,
86,
87,
86,
87,
46,
46,
86,
87,
86,
87,
90,
46,

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

57
57
57
57
58
58
58
58
58
58
58
58
59
59
59
59
59
59
59
59
60
60
60
60
60
60
60
60
61
61
61
61
61
61
61
61
62
62
62
62
62
62
62
62
63
63
63
63
63
63
63
63
64
64
64
64
64
64
64
64

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

86,
87,
86,
87,
86,
87,
86,
87,
11,
97,
86,
87,
86,
87,
46,
102,
2,
2,
8,
5,
3,
105,
3,
3,
12,
46,
46,
46,
46,
46,
17,
17,
107,
107,
46,
46,
4,
4,
46,
46,
46,
46,
60,
60,
79,
46,
46,
46,
15,
15,
38,
38,
15,
38,
38,
40,
46,
46,
46,
19,

86,
87,
86,
87,
86,
87,
86,
87,
11,
97,
86,
87,
86,
87,
46,
102,
2,
2,
8,
6,
3,
106,
3,
5,
3,
46,
46,
46,
46,
46,
46,
17,
107,
107,
46,
46,
4,
4,
46,
46,
46,
46,
60,
60,
60,
46,
46,
46,
15,
15,
38,
38,
15,
15,
38,
46,
46,
46,
46,
19,

86,
87,
86,
87,
86,
87,
16,
95,
16,
97,
16,
98,
16,
100,
16,
103,
2,
2,
8,
5,
3,
104,
3,
6,
3,
46,
46,
46,
46,
104,
46,
7,
107,
7,
46,
46,
4,
4,
46,
46,
46,
46,
60,
60,
46,
46,
46,
46,
38,
16,
38,
38,
15,
38,
15,
46,
46,
46,
46,
19,

86,
87,
86,
87,
86,
87,
94,
95,
94,
97,
16,
98,
16,
100,
94,
103,
2,
2,
8,
5,
3,
104,
3,
3,
3,
46,
46,
46,
46,
104,
46,
7,
107,
7,
46,
46,
4,
4,
46,
46,
46,
46,
60,
60,
46,
46,
46,
46,
15,
38,
16,
38,
15,
38,
38,
46,
46,
46,
19,
19,

86,
87,
86,
87,
86,
87,
16,
96,
16,
96,
46,
46,
16,
101,
16,
96,
2,
104,
8,
5,
3,
104,
3,
3,
7,
46,
46,
46,
46,
104,
17,
7,
107,
7,
46,
46,
4,
46,
46,
46,
46,
46,
60,
60,
46,
46,
46,
46,
15,
38,
15,
38,
38,
38,
16,
46,
46,
46,
19,
19,

86,
87,
86,
87,
86,
87,
46,
11,
46,
11,
46,
11,
99,
11,
46,
11,
2,
104,
8,
6,
3,
104,
3,
3,
5,
46,
46,
46,
46,
104,
17,
5,
107,
5,
46,
46,
4,
46,
46,
46,
46,
46,
60,
79,
46,
46,
46,
46,
15,
38,
38,
38,
15,
38,
40,
46,
46,
46,
19,
19,

86,
87,
86,
87,
86,
87,
16,
38,
16,
11,
16,
11,
16,
11,
16,
11,
2,
104,
3,
5,
3,
104,
3,
3,
6,
46,
46,
46,
46,
104,
17,
6,
107,
6,
46,
46,
4,
46,
46,
46,
46,
46,
60,
79,
46,
46,
46,
46,
15,
16,
15,
15,
38,
16,
40,
46,
46,
46,
19,
19,

86,
87,
86,
87,
86,
87,
16,
11,
16,
11,
16,
11,
16,
11,
16,
46,
2,
104,
3,
5,
3,
46,
3,
12,
46,
46,
46,
46,
46,
104,
17,
16,
107,
46,
46,
46,
4,
46,
46,
46,
46,
46,
60,
79,
46,
46,
46,
46,
38,
16,
15,
15,
15,
16,
40,
46,
46,
46,
19,
108,

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

65
65
65
65
65
65
65
65
66
66
66
66
66
66
66
66
67
67
67
67
67
67
67
67
68
68
68
68
68
68
68
68
69
69
69
69
69
69
69
69
70
70
70
70
70
70
70
70
71
71
71
71
71
71
71
71
72
72
72
72

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

109,
109,
111,
111,
113,
46,
7,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
46,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
46,
15,
7,
15,
15,
7,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
46,
46,

109,
109,
111,
111,
113,
46,
7,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
46,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
46,
46,
7,
15,
15,
7,
5,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
46,
46,

109,
109,
111,
111,
113,
46,
7,
15,
15,
15,
15,
15,
15,
15,
7,
15,
15,
15,
46,
46,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
46,
46,
15,
7,
15,
15,
15,
6,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
46,
46,

109,
109,
111,
111,
46,
46,
7,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
46,
46,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
46,
46,
15,
7,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
15,
15,
15,
15,
15,
46,
46,
46,

109,
110,
111,
112,
46,
46,
7,
15,
15,
15,
15,
15,
15,
15,
7,
15,
15,
46,
46,
46,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
46,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
15,
15,
15,
15,
15,
46,
46,
46,

109,
110,
111,
112,
46,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
46,
46,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
46,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
15,
15,
15,
15,
46,
46,
46,
46,

109,
110,
111,
112,
46,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
46,
46,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
46,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
15,
15,
15,
15,
46,
46,
46,
46,

109,
110,
111,
112,
46,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
46,
46,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
46,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
15,
15,
15,
15,
46,
46,
46,
46,

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

72
72
72
72
73
73
73
73
73
73
73
73
74
74
74
74
74
74
74
74
75
75
75
75
75
75
75
75
76
76
76
76
76
76
76
76
77
77
77
77
77
77
77
77
78
78
78
78
78
78
78
78
79
79
79
79
79
79
79
79

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

15,
15,
46,
46,
114,
114,
114,
82,
82,
115,
115,
115,
15,
15,
15,
116,
116,
116,
117,
117,
117,
117,
46,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
46,
15,
15,
15,
46,
15,
15,
15,
15,
46,
15,
15,
15,

15,
15,
46,
46,
114,
114,
114,
82,
82,
115,
115,
115,
15,
15,
15,
116,
116,
116,
117,
117,
117,
117,
46,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
46,
15,
15,
15,
46,
15,
15,
15,
15,
15,
15,
15,
15,

15,
15,
46,
46,
114,
114,
114,
82,
82,
115,
115,
115,
15,
15,
15,
116,
116,
116,
117,
117,
117,
118,
46,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
15,
15,

15,
46,
46,
46,
114,
114,
114,
82,
82,
115,
115,
115,
15,
15,
15,
116,
116,
116,
117,
117,
117,
46,
46,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
15,
15,

15,
46,
46,
46,
114,
114,
82,
82,
82,
115,
115,
15,
15,
15,
15,
116,
116,
116,
117,
117,
117,
46,
46,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
46,
15,
15,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,

15,
46,
46,
46,
114,
114,
82,
82,
82,
115,
115,
15,
15,
15,
15,
116,
116,
116,
117,
117,
117,
46,
46,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
46,
15,
15,
46,
15,
15,
15,
15,
15,
46,
15,
15,
15,

15,
46,
46,
46,
114,
114,
82,
82,
82,
115,
115,
15,
15,
15,
116,
116,
116,
116,
117,
117,
117,
46,
46,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
46,
15,
15,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,

15,
46,
46,
46,
114,
114,
82,
82,
82,
115,
115,
15,
15,
15,
116,
116,
116,
116,
117,
117,
117,
46,
46,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
46,
15,
15,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,

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

80
80
80
80
80
80
80
80
81
81
81
81
81
81
81
81
82
82
82
82
82
82
82
82
83
83
83
83
83
83
83
83
84
84
84
84
84
84
84
84
85
85
85
85
85
85
85
85
86
86
86
86
86
86
86
86
87
87
87
87

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

15,
46,
15,
15,
15,
15,
15,
15,
46,
46,
46,
119,
114,
114,
83,
15,
15,
15,
46,
15,
2,
5,
5,
5,
15,
121,
8,
46,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
40,
40,
40,
40,
40,
46,
40,

15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
46,
119,
114,
114,
83,
15,
15,
15,
15,
15,
3,
6,
6,
6,
121,
121,
59,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
60,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
40,
40,
40,
40,
40,
40,
40,

15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
46,
119,
114,
83,
83,
15,
15,
15,
15,
15,
3,
5,
15,
5,
121,
60,
59,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
60,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
40,
40,
40,
40,
40,
40,
40,

15,
15,
15,
15,
15,
15,
46,
15,
15,
46,
46,
119,
114,
83,
83,
15,
15,
15,
15,
15,
3,
6,
15,
6,
121,
60,
59,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
59,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
3,
46,
40,
40,
40,
40,
40,
40,
40,

15,
15,
15,
15,
15,
46,
46,
15,
15,
46,
46,
119,
114,
83,
15,
15,
15,
15,
15,
15,
15,
5,
5,
8,
121,
60,
59,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
59,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
59,
46,
40,
40,
40,
40,
40,
40,
40,

15,
15,
15,
15,
15,
15,
46,
15,
15,
46,
46,
119,
114,
83,
46,
15,
15,
15,
15,
15,
59,
6,
6,
5,
121,
60,
59,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
59,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
59,
40,
40,
40,
40,
40,
46,
40,
40,

15,
15,
15,
15,
15,
46,
15,
15,
15,
46,
119,
119,
114,
83,
46,
15,
15,
15,
15,
15,
3,
5,
5,
6,
121,
60,
15,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
59,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
59,
40,
40,
40,
40,
40,
46,
40,
40,

15,
15,
15,
15,
15,
15,
46,
46,
15,
46,
119,
119,
114,
83,
46,
15,
15,
15,
15,
46,
120,
6,
6,
5,
121,
60,
15,
15,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
40,
40,
40,
40,
40,
46,
40,
40,

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

87
87
87
87
88
88
88
88
88
88
88
88
89
89
89
89
89
89
89
89
90
90
90
90
90
90
90
90
91
91
91
91
91
91
91
91
92
92
92
92
92
92
92
92
93
93
93
93
93
93
93
93
94
94
94
94
94
94
94
94

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

40,
40,
15,
15,
46,
46,
46,
46,
15,
15,
15,
15,
85,
85,
15,
15,
15,
46,
46,
46,
15,
15,
15,
15,
114,
114,
15,
15,
15,
15,
15,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
15,
15,
15,
15,
15,
15,
15,
15,
40,
40,
40,
40,

40,
40,
15,
15,
46,
46,
46,
46,
15,
15,
15,
15,
85,
85,
15,
15,
15,
46,
46,
46,
15,
15,
15,
15,
114,
114,
15,
15,
15,
15,
46,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
15,
15,
15,
15,
15,
15,
15,
15,
40,
40,
40,
40,

40,
40,
85,
15,
46,
46,
46,
46,
15,
15,
15,
15,
85,
15,
15,
15,
15,
46,
46,
46,
15,
15,
15,
15,
114,
15,
15,
15,
15,
15,
46,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
15,
15,
15,
15,
15,
15,
15,
15,
40,
40,
40,
40,

40,
40,
85,
15,
46,
46,
46,
46,
15,
15,
15,
15,
85,
15,
15,
15,
15,
46,
46,
46,
15,
15,
15,
15,
114,
15,
15,
15,
15,
15,
46,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
40,
40,
40,
40,

40,
40,
85,
15,
46,
46,
46,
46,
15,
15,
15,
15,
85,
15,
15,
15,
46,
46,
46,
46,
15,
15,
15,
46,
114,
15,
15,
15,
15,
15,
46,
46,
15,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
40,
40,
40,
40,

40,
40,
85,
15,
46,
46,
46,
46,
15,
15,
15,
46,
85,
15,
15,
15,
46,
46,
46,
46,
15,
15,
15,
46,
114,
15,
15,
15,
15,
15,
46,
46,
15,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
40,
40,
40,
40,

40,
40,
15,
15,
46,
46,
46,
46,
15,
15,
15,
46,
85,
15,
15,
15,
46,
46,
46,
46,
15,
15,
15,
46,
114,
15,
15,
15,
15,
15,
46,
46,
15,
46,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
15,
46,
15,
15,
15,
15,
40,
40,
40,
40,

40,
46,
15,
15,
46,
46,
46,
46,
15,
15,
15,
46,
85,
15,
15,
15,
46,
46,
46,
46,
15,
15,
15,
15,
114,
15,
15,
15,
15,
15,
46,
46,
15,
46,
15,
15,
15,
15,
15,
46,
15,
15,
15,
15,
15,
15,
46,
15,
15,
15,
15,
46,
15,
15,
15,
46,
40,
40,
40,
40,

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

95
95
95
95
95
95
95
95
96
96
96
96
96
96
96
96
97
97
97
97
97
97
97
97
98
98
98
98
98
98
98
98
99
99
99
99
99
99
99
99
100
100
100
100
100
100
100
100
101
101
101
101
101
101
101
101
102
102
102
102

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

40,
46,
46,
46,
40,
40,
40,
40,
40,
46,
46,
46,
122,
122,
122,
122,
122,
122,
122,
122,
123,
123,
123,
123,
123,
123,
123,
123,
40,
40,
40,
40,
40,
40,
46,
46,
16,
46,
46,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,

40,
46,
46,
46,
40,
40,
40,
40,
40,
46,
46,
46,
122,
122,
122,
122,
122,
122,
122,
122,
123,
123,
123,
123,
123,
123,
123,
123,
40,
40,
40,
40,
40,
40,
46,
46,
16,
46,
46,
46,
40,
7,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,

40,
46,
46,
46,
40,
40,
40,
40,
40,
46,
46,
46,
122,
122,
122,
122,
122,
122,
122,
122,
123,
123,
123,
123,
123,
123,
123,
123,
40,
40,
40,
40,
40,
40,
46,
46,
16,
46,
46,
46,
40,
40,
40,
40,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
46,

40,
46,
46,
46,
40,
40,
40,
40,
40,
46,
46,
46,
122,
122,
122,
122,
122,
122,
122,
122,
123,
123,
123,
123,
123,
123,
123,
123,
40,
40,
40,
40,
40,
40,
46,
46,
16,
46,
16,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
46,

40,
46,
46,
46,
40,
40,
40,
40,
46,
46,
46,
46,
122,
122,
122,
122,
122,
122,
122,
122,
123,
123,
123,
123,
123,
123,
123,
123,
40,
40,
40,
40,
40,
40,
46,
46,
16,
46,
16,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
46,

40,
46,
46,
46,
40,
40,
40,
40,
46,
46,
46,
46,
122,
122,
122,
122,
122,
122,
122,
122,
123,
123,
123,
123,
123,
123,
123,
123,
40,
40,
40,
40,
40,
40,
46,
46,
16,
46,
16,
46,
40,
40,
40,
46,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
46,

46,
46,
46,
46,
40,
40,
40,
40,
46,
46,
46,
46,
122,
122,
122,
122,
122,
122,
122,
122,
123,
123,
123,
123,
123,
123,
123,
123,
40,
40,
40,
40,
40,
46,
46,
46,
16,
46,
16,
60,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
46,

46,
46,
46,
46,
40,
40,
40,
40,
46,
46,
46,
46,
122,
122,
122,
122,
122,
122,
122,
122,
123,
123,
123,
123,
123,
123,
123,
123,
40,
40,
40,
40,
40,
46,
46,
46,
46,
46,
16,
40,
40,
40,
46,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
46,

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

102
102
102
102
103
103
103
103
103
103
103
103
104
104
104
104
104
104
104
104
105
105
105
105
105
105
105
105
106
106
106
106
106
106
106
106
107
107
107
107
107
107
107
107
108
108
108
108
108
108
108
108
109
109
109
109
109
109
109
109

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

46,
46,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
46,
40,
40,
40,
40,
40,
40,
40,
40,
46,
40,
40,
40,
40,
40,
40,
46,
46,
46,
46,
46,
40,
40,
46,
46,
46,
46,
60,
46,
3,
6,
6,
46,
3,
8,
3,
3,
40,
40,
40,
40,
40,
40,

46,
46,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
46,
40,
40,
40,
40,
40,
40,
40,
40,
46,
40,
40,
40,
40,
40,
40,
46,
46,
46,
46,
46,
40,
40,
46,
46,
46,
46,
60,
46,
8,
5,
5,
3,
3,
5,
3,
4,
40,
40,
40,
40,
40,
40,

46,
46,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
46,
46,
46,
46,
40,
40,
46,
46,
46,
46,
60,
46,
8,
6,
6,
3,
3,
6,
7,
3,
40,
40,
40,
40,
40,
40,

46,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
46,
46,
46,
46,
40,
40,
46,
46,
46,
46,
60,
46,
12,
5,
5,
3,
46,
5,
8,
3,
46,
40,
40,
40,
40,
40,

46,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
46,
46,
46,
46,
40,
46,
46,
46,
46,
46,
46,
46,
12,
6,
6,
3,
3,
6,
7,
46,
40,
40,
40,
40,
40,
40,

46,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
46,
46,
46,
46,
40,
46,
46,
46,
46,
46,
46,
46,
5,
5,
46,
12,
3,
5,
7,
46,
46,
40,
40,
40,
40,
40,

46,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
5,
46,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
46,
46,
46,
46,
40,
46,
46,
46,
46,
46,
46,
46,
6,
6,
46,
12,
3,
6,
7,
46,
40,
40,
40,
40,
40,
40,

46,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
6,
46,
46,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
46,
46,
46,
46,
40,
46,
46,
46,
46,
46,
46,
46,
5,
5,
46,
12,
3,
3,
46,
46,
40,
40,
40,
40,
40,
40,

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

110
110
110
110
110
110
110
110
111
111
111
111
111
111
111
111
112
112
112
112
112
112
112
112
113
113
113
113
113
113
113
113
114
114
114
114
114
114
114
114
115
115
115
115
115
115
115
115
116
116
116
116
116
116
116
116
117
117
117
117

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

40,
40,
40,
40,
46,
5,
9,
9,
3,
10,
10,
10,
11,
13,
13,
13,
46,
40,
59,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
46,
46,
46,
4,
7,
46,
46,
};

40,
40,
40,
40,
3,
6,
9,
9,
10,
10,
10,
10,
13,
13,
13,
13,
3,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
46,
46,
46,
4,
7,
46,
46,

40,
40,
40,
40,
3,
3,
9,
3,
10,
10,
10,
10,
13,
13,
13,
13,
5,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
7,
7,
46,
46,

40,
40,
40,
40,
3,
7,
9,
3,
10,
10,
10,
5,
13,
13,
13,
5,
6,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
11,
7,
46,
46,

40,
40,
40,
40,
4,
3,
9,
7,
10,
10,
10,
3,
13,
13,
13,
7,
3,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
15,
7,
46,
46,

40,
40,
40,
46,
3,
8,
9,
7,
10,
10,
10,
6,
13,
13,
13,
6,
3,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
40,
46,
4,
15,
46,
15,

40,
40,
40,
46,
3,
3,
9,
7,
10,
10,
10,
11,
13,
13,
13,
7,
40,
40,
40,
40,
40,
40,
40,
59,
40,
40,
40,
40,
40,
40,
40,
46,
4,
15,
46,
46,

40,
40,
40,
104,
3,
3,
9,
3,
10,
10,
10,
12,
13,
13,
13,
46,
40,
40,
40,
40,
40,
40,
40,
59,
40,
40,
40,
46,
40,
40,
40,
46,
46,
46,
46,
46

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

117
117
117
117
118
118
118
118
118
118
118
118
119
119
119
119
119
119
119
119
120
120
120
120
120
120
120
120
121
121
121
121
121
121
121
121

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

/* The A table has 124 entries for a total of 496 bytes. */


const uint32
0x0001000F,
0x0004000F,
0x0004000C,
0x00000018,
0x0006001A,
0x00000015,
0x00000016,
0x00000019,
0x00000014,
0x00036009,
0x0827FE01,
*/
0x0000001B,
0x00050017,
0x0817FE02,
l 31 */
0x0000000C,
0x0000001C,
0x00070002,

js_A[] =
/*
0
/*
1
/*
2
/*
3
/*
4
/*
5
/*
6
/*
7
/*
8
/*
9
/* 10

{
Cc, ignorable */
Cc, whitespace */
Zs, whitespace */
Po */
Sc, currency */
Ps */
Pe */
Sm */
Pd */
Nd, identifier part, decimal 16 */
Lu, hasLower (add 32), identifier start, supradecimal 31

/*
/*
/*

11
12
13

Sk */
Pc, underscore */
Ll, hasUpper (subtract 32), identifier start, supradecima

/*
/*
/*

14
15
16

Zs */
So */
Ll, identifier start */

0x0000600B, /*
0x0000500B, /*
0x0000800B, /*
0x08270001, /*
0x08170002, /*
0xE1D70002, /*
0x00670001, /*
0x00570002, /*
0xCE670001, /*
0x3A170002, /*
0xE1E70001, /*
0x4B170002, /*
0x34A70001, /*
0x33A70001, /*
0x33670001, /*
0x32A70001, /*
0x32E70001, /*
0x33E70001, /*
0x34E70001, /*
0x34670001, /*
0x35670001, /*
0x00070001, /*
0x36A70001, /*
0x00070005, /*
0x36670001, /*
0x36E70001, /*
0x00AF0001, /*
0x007F0003, /*
entifier start */
0x009F0002, /*
0x00000000, /*
0x34970002, /*
0x33970002, /*
0x33570002, /*
0x32970002, /*
0x32D70002, /*
0x33D70002, /*
0x34570002, /*
0x34D70002, /*
0x35570002, /*
0x36970002, /*
0x36570002, /*
0x36D70002, /*
0x00070004, /*
0x00030006, /*
0x09A70001, /*
0x09670001, /*
0x10270001, /*
0x0FE70001, /*
0x09970002, /*
0x09570002, /*
0x10170002, /*
0x0FD70002, /*
0x0F970002, /*
0x0E570002, /*
0x0BD70002, /*
0x0D970002, /*
0x15970002, /*
0x14170002, /*
0x14270001, /*

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

No,
No,
No,
Lu,
Ll,
Ll,
Lu,
Ll,
Lu,
Ll,
Lu,
Ll,
Lu,
Lu,
Lu,
Lu,
Lu,
Lu,
Lu,
Lu,
Lu,
Lu,
Lu,
Lo,
Lu,
Lu,
Lu,
Lt,

decimal 16 */
decimal 8 */
strange */
hasLower (add 32), identifier start */
hasUpper (subtract 32), identifier start */
hasUpper (subtract -121), identifier start */
hasLower (add 1), identifier start */
hasUpper (subtract 1), identifier start */
hasLower (add -199), identifier start */
hasUpper (subtract 232), identifier start */
hasLower (add -121), identifier start */
hasUpper (subtract 300), identifier start */
hasLower (add 210), identifier start */
hasLower (add 206), identifier start */
hasLower (add 205), identifier start */
hasLower (add 202), identifier start */
hasLower (add 203), identifier start */
hasLower (add 207), identifier start */
hasLower (add 211), identifier start */
hasLower (add 209), identifier start */
hasLower (add 213), identifier start */
identifier start */
hasLower (add 218), identifier start */
identifier start */
hasLower (add 217), identifier start */
hasLower (add 219), identifier start */
hasLower (add 2), hasTitle, identifier start */
hasUpper (subtract 1), hasLower (add 1), hasTitle, id

45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

Ll, hasUpper (subtract 2), hasTitle, identifier start */


unassigned */
Ll, hasUpper (subtract 210), identifier start */
Ll, hasUpper (subtract 206), identifier start */
Ll, hasUpper (subtract 205), identifier start */
Ll, hasUpper (subtract 202), identifier start */
Ll, hasUpper (subtract 203), identifier start */
Ll, hasUpper (subtract 207), identifier start */
Ll, hasUpper (subtract 209), identifier start */
Ll, hasUpper (subtract 211), identifier start */
Ll, hasUpper (subtract 213), identifier start */
Ll, hasUpper (subtract 218), identifier start */
Ll, hasUpper (subtract 217), identifier start */
Ll, hasUpper (subtract 219), identifier start */
Lm, identifier start */
Mn, identifier part */
Lu, hasLower (add 38), identifier start */
Lu, hasLower (add 37), identifier start */
Lu, hasLower (add 64), identifier start */
Lu, hasLower (add 63), identifier start */
Ll, hasUpper (subtract 38), identifier start */
Ll, hasUpper (subtract 37), identifier start */
Ll, hasUpper (subtract 64), identifier start */
Ll, hasUpper (subtract 63), identifier start */
Ll, hasUpper (subtract 62), identifier start */
Ll, hasUpper (subtract 57), identifier start */
Ll, hasUpper (subtract 47), identifier start */
Ll, hasUpper (subtract 54), identifier start */
Ll, hasUpper (subtract 86), identifier start */
Ll, hasUpper (subtract 80), identifier start */
Lu, hasLower (add 80), identifier start */

0x0C270001,
0x0C170002,
0x00034009,
0x00000007,
0x00030008,
0x00037409,
0x00005A0B,
0x00006E0B,
0x0000740B,
0x0000000B,
0xFE170002,
0xFE270001,
0xED970002,
0xEA970002,
0xE7170002,
0xE0170002,
0xE4170002,
0xE0970002,
0xFDD70002,
0xEDA70001,
0xFDE70001,
0xEAA70001,
0xE7270001,
0xFE570002,
0xE4270001,
0xFE670001,
0xE0270001,
0xE0A70001,
0x00010010,
0x0004000D,
0x0004000E,
0x0000400B,
0x0000440B,
0x0427420A,
0x0427800A,
0x0417620A,
*/
0x0417800A,
0x0007800A,
0x0000420B,
0x0000720B,
0x06A0001C,
0x0690001C,
0x00006C0B,
0x0000560B,
0x0007720A,
0x0007400A,
0x00000013,
0x00000012
};

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

76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111

Lu, hasLower (add 48), identifier start */


Ll, hasUpper (subtract 48), identifier start */
Nd, identifier part, decimal 0 */
Me */
Mc, identifier part */
Nd, identifier part, decimal 26 */
No, decimal 13 */
No, decimal 23 */
No, decimal 26 */
No */
Ll, hasUpper (subtract -8), identifier start */
Lu, hasLower (add -8), identifier start */
Ll, hasUpper (subtract -74), identifier start */
Ll, hasUpper (subtract -86), identifier start */
Ll, hasUpper (subtract -100), identifier start */
Ll, hasUpper (subtract -128), identifier start */
Ll, hasUpper (subtract -112), identifier start */
Ll, hasUpper (subtract -126), identifier start */
Ll, hasUpper (subtract -9), identifier start */
Lu, hasLower (add -74), identifier start */
Lu, hasLower (add -9), identifier start */
Lu, hasLower (add -86), identifier start */
Lu, hasLower (add -100), identifier start */
Ll, hasUpper (subtract -7), identifier start */
Lu, hasLower (add -112), identifier start */
Lu, hasLower (add -7), identifier start */
Lu, hasLower (add -128), identifier start */
Lu, hasLower (add -126), identifier start */
Cf, ignorable */
Zl, whitespace */
Zp, whitespace */
No, decimal 0 */
No, decimal 2 */
Nl, hasLower (add 16), identifier start, decimal 1 */
Nl, hasLower (add 16), identifier start, strange */
Nl, hasUpper (subtract 16), identifier start, decimal 17

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

112
113
114
115
116
117
118
119
120
121
122
123

Nl, hasUpper (subtract 16), identifier start, strange */


Nl, identifier start, strange */
No, decimal 1 */
No, decimal 25 */
So, hasLower (add 26) */
So, hasUpper (subtract 26) */
No, decimal 22 */
No, decimal 11 */
Nl, identifier start, decimal 25 */
Nl, identifier start, decimal 0 */
Cs */
Co */

const jschar js_uriReservedPlusPound_ucstr[] =


{';', '/', '?', ':', '@', '&', '=', '+', '$', ',',
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',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
'-', '_', '.', '!', '~', '*', '\'', '(', ')', 0};

'#', 0};
'K',
'X',
'k',
'x',

'L',
'Y',
'l',
'y',

'M',
'Z',
'm',
'z',

#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
jschar
};

length;
*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)
#define JS_ISLOWER(c)

(JS_CTYPE(c) == JSCT_UPPERCASE_LETTER)
(JS_CTYPE(c) == JSCT_LOWERCASE_LETTER)

#define JS_TOUPPER(c)
>> 22) : (c))
#define JS_TOLOWER(c)
>> 22) : (c))

((JS_CCODE(c) & 0x00100000) ? (c) - ((int32)JS_CCODE(c)

#define JS_TOCTRL(c)

((c) ^ 64)

/* Shorthands for ASCII


#define JS7_ISDEC(c)
#define JS7_UNDEC(c)
#define JS7_ISHEX(c)
#define JS7_UNHEX(c)
#define JS7_ISLET(c)

(7-bit) decimal and hex conversion. */


((c) < 128 && isdigit(c))
((c) - '0')
((c) < 128 && isxdigit(c))
(uintN)(isdigit(c) ? (c) - '0' : 10 + tolower(c) - 'a')
((c) < 128 && isalpha(c))

((JS_CCODE(c) & 0x00200000) ? (c) + ((int32)JS_CCODE(c)


/* XXX unsafe! requires uppercase 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
extern
extern
extern
extern
extern
extern

const
const
const
const
const
const
const

char
char
char
char
char
char
char

js_escape_str[];
js_unescape_str[];
js_uneval_str[];
js_decodeURI_str[];
js_encodeURI_str[];
js_decodeURIComponent_str[];
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
#define
#define
#define

JS_EXTERN_API(__type) extern __type


JS_EXPORT_API(__type) __type
JS_EXTERN_DATA(__type) extern __type
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)
# else
#
define JS_IMPORT_API(__x)
# endif
#else
#
define JS_IMPORT_API(__x)
#endif

__x
__x
JS_EXPORT_API (__x)

#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)
#define JS_FRIEND_DATA(t)

JS_PUBLIC_API(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
#define JS_END_EXTERN_C
#else
#define JS_BEGIN_EXTERN_C
#define JS_END_EXTERN_C
#endif

extern "C" {
}

/***********************************************************************
** 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_C
OMPILE)
#
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 b

y IBM
* described herein are Copyright (c) International Business Machines Corporatio
n, 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 !=
{
unsigned
/* count
short
register

NULL )
char*
curdst = &dst[1];
down so test it loop is faster */
overflow = 255;
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
#include
#include
#include
#include

<string.h>
"jstypes.h"
"jsutil.h" /* Added by JSIFY */
"jsprf.h"
"jsapi.h"

#include
#include
#include
#include

"jscntxt.h"
"jsobj.h"
"jsstr.h"
"jsxdrapi.h"

/* js_XDRObject */

#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
#define MEM_PRIV(xdr)

8192
((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_raw,
mem_seek,

mem_getbytes,
mem_tell,

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

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
JSBool
JSBool
JSBool
JSBool
void *
JSBool
uint32
void
} JSXDROps;

JSXDROps {
(*get32)(JSXDRState *, uint32 *);
(*set32)(JSXDRState *, uint32 *);
(*getbytes)(JSXDRState *, char **, uint32);
(*setbytes)(JSXDRState *, char **, uint32);
(*raw)(JSXDRState *, uint32);
(*seek)(JSXDRState *, int32, JSXDRWhence);
(*tell)(JSXDRState *);
(*finalize)(JSXDRState *);

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
static unsigned long

dstLocalBaseMicroseconds;
gJanuaryFirst1970Seconds;

static void MacintoshInitializeTime(void)


{
uint64
upTime;
unsigned long
currentLocalTimeSeconds,
startupTimeSeconds;
uint64
startupTimeMicroSeconds;
uint32
upTimeSeconds;
uint64
oneMillion, upTimeSecondsLong, microSeco
ndsToSeconds;
DateTimeRec
firstSecondOfUnixTime;
/*
* Figure out in local time what time the machine started up. This informati
on 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 a
nd 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, tim
e 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
#define G2037GMTMICROLOW

0x00e45fab /* micro secs to 2037 high */


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 util
ities 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) + gJanuaryFirst197
0Seconds, 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
prtm->tm_sec
prtm->tm_min
prtm->tm_hour
prtm->tm_mday
prtm->tm_mon
prtm->tm_wday
prtm->tm_year
prtm->tm_yday

=
=
=
=
=
=
=
=
=

0L;
(JSInt8)seconds;
(JSInt8)minutes;
(JSInt8)hours;
(JSInt8)mday;
(JSInt8)month;
(JSInt8)wday;
(JSInt16)year;
(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
#define PRMJ_USEC_PER_MSEC

1000000L
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
#define _APS_NEXT_COMMAND_VALUE
#define _APS_NEXT_CONTROL_VALUE
#define _APS_NEXT_SYMED_VALUE
#endif
#endif
**** End of resource.h ****

101
40001
1000
101

Você também pode gostar