Escolar Documentos
Profissional Documentos
Cultura Documentos
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)
{
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 */
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;
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);
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;
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 : "",
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,
1,
0,
0,
3,
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}")
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")
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,
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")
/*
* 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);
*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},
};
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);
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;
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_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) {
#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)
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)) {
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 - (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;
}
}
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);
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,
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 *)
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,
#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...
-*- 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
0x0
0x1
0x2
0x4
0x6
/*
/*
/*
/*
/*
*/
(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))
\
\
#define JSVAL_UNLOCK(cx,v)
: 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)
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
/*
*
*
*
*
*
*
* 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
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);
/*
JSConvertOp
JSFinalizeOp
convert;
finalize;
0x01
0x02
0x04
0x08
0x10
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
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;
};
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 *)
/************************************************************************/
/*
* 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
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);
/*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
* 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;
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
};
/*
* 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
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"
/*
* 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,
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
{
*next;
base;
limit;
avail;
/*
/*
/*
/*
#ifdef JS_ARENAMETER
typedef struct JSArenaStats JSArenaStats;
struct JSArenaStats {
JSArenaStats *next;
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;
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
};
#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)
\
\
\
\
\
\
\
\
\
\
\
\
JS_END_MACRO
#define JS_ARENA_GROW(p, pool, size, incr)
JS_ARENA_GROW_CAST(p, void *, pool, size, incr)
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
#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 */
\
\
\
\
\
\
\
\
/*
* 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
* 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;
jschar
jschar
JSString
JSString
comma_space_ucstr[]
comma_ucstr[]
comma_space
comma
=
=
=
=
#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, //
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;
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.
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},
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
/*
* 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";
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 *
{
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);
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
/*
/*
/*
/*
/*
entry;
flags;
kwindex;
number;
/*
/*
/*
/*
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
struct JSAtomList {
JSAtomListElement
JSHashTable
((JSAtom *) (ale)->entry.key)
((jsatomid) (ale)->entry.value)
((JSOp) (ale)->entry.value)
((JSAtomListElement *) (ale)->entry.next)
*list;
*table;
jsuint
count;
};
#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;
struct JSAtomState {
JSRuntime
JSHashTable
jsatomid
jsatomid
*runtime;
*table;
number;
interns;
/*
/*
/*
/*
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;
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
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"
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,
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 ****
/*
/*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
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 */
-*- 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
/*
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;
}
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;
}
#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;
jsdouble
jsdouble
jsdouble
*jsNaN;
*jsNegativeInfinity;
*jsPositiveInfinity;
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;
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 ****
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
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
#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
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
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
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
#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
/*
/*
/*
/*
/*
/*
#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
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
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
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
#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
/*
/*
/*
/*
/*
/*
/*
/*
/*
#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
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
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
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
#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
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
#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
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
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
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
#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
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
#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
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
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
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
#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
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
#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
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
#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);
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.
*/
/*
*
*
*
*
*
*
*/
#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"
*
*
*
*
*
*
*/
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)
#define InLeapYear(t)
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;
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;
}
}
}
}
}
}
}
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;
/*
}
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)
{
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,
}
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
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) {
/*
* 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 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_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"
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;
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;
}
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 != (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;
}
/************************************************************************/
{
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
}
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) {
#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
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 *)
JSPD_ENUMERATE
JSPD_READONLY
JSPD_PERMANENT
JSPD_ALIAS
JSPD_ARGUMENT
JSPD_VARIABLE
0x01
0x02
0x04
0x08
0x10
0x20
/*
/*
/*
/*
/*
/*
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
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;
}
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,
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",
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;
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
#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);
/*
*
*
*
*
*/
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
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];
};
/*
/*
/*
/*
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;
/*
* 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;
}
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
};
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;
/*
*
*
*
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;
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,
/*
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;
}
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 */
char *numEnd;
/
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 */
-*- 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
*
* 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"
{
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";
\
\
\
\
\
\
\
\
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
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;
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.
*/
}
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;
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;
}
}
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);
/*
*
*
*
*
*
*
if there's a catchguard
" "
non-local; finally applies
catchguard, the last
to rethrow code. This
if appropriate, and is
for capturing exceptions
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
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;
}
/*
* 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:
}
}
/* 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;
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) {
"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;
/* 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));
-*- 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
"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)
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
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;
};
#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
\
\
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
#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)
/*
/*
/*
/*
((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)
/*
* 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)
#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))
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
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;
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 (!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 */
* 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):
#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
#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 *
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;
}
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
((
((
((
((
((
((
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;
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;
}
}
/*
/*
/*
*
*
*
*/
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;
}
/* 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);
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_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,
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;
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){
{
{
{
{
{
{
{
{
"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,
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:
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
#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)
/*
/*
/*
/*
/*
/*
/*
/*
#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
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:
(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;
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;
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,
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);
}
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)
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);
}
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.
*/
} 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
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
*/
#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
#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)
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
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;
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
{
JSHashNumber num = (JSHashNumber) key;
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;
}
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();
}
\
\
\
\
\
\
\
\
\
#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);
0
/* JSObject */
1
/* JSString */
2
/* jsdouble */
3
/* JSString w/ external chars */
3
/* type index bits */
JS_BIT(GCX_NTYPES_LOG2)
#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 */
\
\
\
\
\
\
\
\
\
#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; /*
} 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 */
}
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++) {
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,
fprintf(fp,
fprintf(fp,
fprintf(fp,
fprintf(fp,
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);
0
1
struct JSHashEntry {
JSHashEntry
JSHashNumber
const void
void
};
*next;
keyHash;
*key;
*value;
/*
/*
/*
/*
**buckets;
nentries;
shift;
keyHash;
keyCompare;
valueCompare;
*allocOps;
*allocPriv;
/*
/*
/*
/*
/*
/*
/*
/*
nlookups;
nsteps;
ngrows;
nshrinks;
/*
/*
/*
/*
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,
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.
#
#
#
#
#
"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 . "|"
}
#
}
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);
*
* 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
(JSSLOT_FREE(&prop_iterator_class))
(JSSLOT_FREE(&prop_iterator_class))
(JSSLOT_START)
(JSSLOT_START)
}
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
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
/*
* 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;
*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;
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;
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];
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;
\
\
\
\
\
\
\
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
\
\
\
\
/*
* 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)
\
\
\
\
\
\
\
\
\
\
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;
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
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, <mp);
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);
}
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),
#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);
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.
*
}
/* 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
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);
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
C stack.
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
JSPackedBool
JSStackFrame
reserved;
*dormantNext;
};
typedef struct JSInlineFrame {
JSStackFrame
frame;
void
*mark;
void
*hookData;
} JSInlineFrame;
/* base struct */
/* mark before inline frame */
/* debugger call hook data */
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
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
#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
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
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
#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
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
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));
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));
fd_cos cos
fd_sin sin
fd_exp exp
fd_sqrt sqrt
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
#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
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));
double
double
double
double
fd_ceil __P((double));
fd_fabs __P((double));
fd_floor __P((double));
fd_fmod __P((double, double));
#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)
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
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
/*
*
*
*
*
*
*
*
*
*
*
*
*
*
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 -**
"jstypes.h"
"pratom.h"
"prlock.h"
"prcvar.h"
"jshash.h" /* Added by JSIFY */
#include "jsprvtd.h"
#include "jspubtd.h"
jsword
JSFatLock
} JSThinLock;
owner;
*fat;
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)
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)
0
1
1
\
\
\
\
\
#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 ****
/*
/*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
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;
}
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;
rp->lo = n0;
rp->hi = n1;
}
} else {
JSInt64 m;
/*
* Normalize.
*/
rsh = 32 - lsh;
b.hi
b.lo
n2 =
n1 =
n0 =
#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) \
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
==
==
!=
>>
op, b)
op, b)
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)
#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)
} \
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)
#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 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;
}
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;
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";
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},
{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
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)
/*
* 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.
#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;
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
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;
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;
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
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. */
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);
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)) {
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.
/*
/*
/*
/*
#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
\
\
#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),
\
\
\
\
#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))
\
\
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 *
#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,
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;
/*
/*
/*
/*
/*
/*
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);
SprintStack {
sprinter;
*offsets;
*opcodes;
top;
*printer;
/*
/*
/*
/*
/*
/* 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;
}
*/
#define DECOMPILE_CODE(pc,nb)
#define POP_STR()
#define LOCAL_ASSERT(expr)
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) {
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);
} 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:
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 {
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)
{
*
* 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 -**
0
1
2
#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
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
* 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
*name;
*token;
length;
nuses;
JS bytecode name */
JS source literal or null */
length including opcode byte */
arity, -1 if variadic */
\
\
int8
uint8
uint32
ndefs;
prec;
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)
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
val name
image
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,
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
1, 2, 1, 5, JOF_BYTE)
1, 2, 1, 5, JOF_BYTE)
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 |
NULL,
3, 0, 1, 12, JOF_CONST)
OPDEF(JSOP_POP,
81, "pop",
NULL,
1, 1, 0, 0, JOF_BYTE)
1, 1, 1, 10, JOF_BYTE)
1, 0, 0, 0, JOF_BYTE)
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",
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)
NULL,
1, 0, 0, 0, JOF_BYTE)
3, 0, 1, 0, JOF_JUMP)
1, 1, 0, 0, JOF_BYTE)
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)
/*
* 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|
3, 0, 1, 12, JOF_CONST)
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 ****
#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,
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
#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;
*/
/***** 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) {
* 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:
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
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
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;
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.
*/
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;
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 *
(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);
}
} 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);
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;
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 ****
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
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
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
* 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;
*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;
/*
/*
/*
/*
/*
*kid1;
*kid2;
*kid3;
/*
/*
/*
/*
*kid;
num;
*atom;
*expr;
slot;
attrs;
/*
/*
/*
/*
/*
dval;
*pn_next;
};
#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
#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
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
/*
* 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_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 */
/* 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;
}
}
/* Zero filling */
}
}
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(", &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)) {
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++;
}
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 ){
case TYPE_INT32:
break;
break;
case TYPE_UINT32:
break;
case TYPE_INT64:
break;
case TYPE_UINT64:
break;
case TYPE_STRING:
break;
case TYPE_INTSTR:
break;
case TYPE_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:
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,
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.
*/
* 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
0
1
struct JSPRHashEntry {
JSPRHashEntry
*next;
JSPRHashNumber
keyHash;
const void
*key;
void
*value;
};
struct JSPRHashTable {
JSPRHashEntry
JSPRUint32
JSPRUint32
**buckets;
nentries;
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);
* 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;
#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);
/* Scalar typedefs. */
typedef uint16
jschar;
typedef int32
jsint;
typedef uint32
jsuint;
typedef float64 jsdouble;
typedef jsword
jsval;
typedef jsword
jsid;
typedef int32
jsrefcount;
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,
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,
/*
/*
/*
/*
REOP_EOLONLY
REOP_UCFLAT
REOP_UCFLAT1
REOP_UCCLASS
=
=
=
=
26,
27,
28,
29,
/*
/*
/*
/*
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,
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
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
#define
#define
#define
#define
#define
RENODE_NONEMPTY
RENODE_ISNEXT
RENODE_GOODNEXT
RENODE_ISJOIN
RENODE_REALLOK
RENODE_MINIMAL
0x04
0x08
0x10
0x20
0x40
0x80
/*
/*
/*
/*
/*
/*
"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 |= flags & RENODE_NONEMPTY;
return ren;
}
/*
* item:
assertion
*
quantatom
*
* assertion: '^'
*
*
*
'$'
*
*
*
'\b'
*
'\B'
*/
static RENode *
ParseItem(CompilerState *state)
{
const jschar *cp;
RENode *ren;
REOp op;
cp = state->cp;
if (cp < state->cpend)
switch (*cp) {
case '^':
state->cp = cp + 1;
ren = NewRENode(state, REOP_BOL, NULL);
if (ren)
ren->flags |= RENODE_ANCHORED;
return ren;
case '$':
state->cp = cp + 1;
return NewRENode(state,
(cp == state->cpbegin ||
((cp[-1] == '(' || cp[-1] == '|') && /*balance)*/
(cp - 1 == state->cpbegin || cp[-2] != '\\')))
? REOP_EOLONLY
: REOP_EOL,
NULL);
case '\\':
switch (*++cp) {
case 'b':
op = REOP_WBDRY;
break;
case 'B':
op = REOP_WNONBDRY;
break;
default:
return ParseQuantAtom(state);
}
/*
* Word boundaries and non-boundaries are flagged as non-empty so th
ey
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:
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
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_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;
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;
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
* 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;
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) {
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;
}
}
\
\
\
\
\
\
\
\
\
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
{
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:
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;
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" : "",
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
}
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;
};
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
* 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;
uintN
lastIndex;
uintN
parenCount;
uint8
flags;
struct RENode *ren;
/*
/*
/*
/*
};
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.
*
\
\
#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++) {
/*
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;
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); }
\
\
\
\
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)) {
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];
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 -**
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
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;
struct JSTokenPos {
JSTokenPtr
JSTokenPtr
};
begin;
end;
struct JSToken {
JSTokenType
JSTokenPos
jschar
union {
struct {
JSOp
JSAtom
} s;
jsdouble
} u;
type;
pos;
*ptr;
op;
*atom;
dval;
};
#define t_op
#define t_atom
#define t_dval
u.s.op
u.s.atom
u.dval
256
#define NTOKENS
#define NTOKENS_MASK
4
(NTOKENS-1)
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
/*
/*
/*
/*
/*
/*
/*
/*
* 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)
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)
/*
/*
/*
/*
((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;
};
/*
/*
/*
/*
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;
};
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
-*- 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. */
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, ¬elen) ||
/* 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. */
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;
JSVersion
JSAtomMap
const char
uintN
uintN
jssrcnote
JSTryNote
JSPrincipals
JSObject
version;
atomMap;
*filename;
lineno;
depth;
*notes;
*trynotes;
*principals;
*object;
/*
/*
/*
/*
/*
/*
/*
/*
/*
};
#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")
MSG_DEF(JSSMSG_UNEXPECTED_EOF,
}")
MSG_DEF(JSSMSG_DOEXP_USAGE,
)
#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>
-*- 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)
-- 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 */
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 */
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;
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 (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;
{
/*
/*
/*
/*
/*
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;
/*
/*
/*
/*
/*
/*
/*
/*
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) {
*
* "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>' */
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);
}
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},
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->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);
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
* 4 bits
* 5 bits
*/
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
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
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
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
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
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
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
112
113
114
115
116
117
118
119
120
121
122
123
'#', 0};
'K',
'X',
'k',
'x',
'L',
'Y',
'l',
'y',
'M',
'Z',
'm',
'z',
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;
-*- 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
length;
*chars;
struct JSSubString {
size_t
length;
const jschar
*chars;
};
extern jschar
js_empty_ucstr[];
extern JSSubString js_EmptySubString;
/* Unicode character attribute lookup tables. */
\
\
\
\
\
#define JS_ISALNUM(c)
\
\
\
\
\
\
\
\
\
\
(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)
\
\
\
\
\
\
\
\
\
\
\
\
(JS_CTYPE(c) == JSCT_DECIMAL_DIGIT_NUMBER)
(JS_CTYPE(c) == JSCT_UPPERCASE_LETTER)
(JS_CTYPE(c) == JSCT_LOWERCASE_LETTER)
#define JS_TOUPPER(c)
>> 22) : (c))
#define JS_TOLOWER(c)
>> 22) : (c))
#define JS_TOCTRL(c)
((c) ^ 64)
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[];
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 ****
/*
/*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
#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
#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)
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;
/************************************************************************
** 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;
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 */
-*- 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
<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))
\
\
\
\
\
\
\
\
/* 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 (!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];
}
*/
#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
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 *)
\
\
\
-*- 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;
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 *)<ime,0,sizeof(ltime));
ltime.tm_mday = 2;
ltime.tm_year = 70;
#ifdef SUNOS4
ltime.tm_zone = 0;
ltime.tm_gmtoff = 0;
return timelocal(<ime) - (24 * 3600);
#else
return mktime(<ime) - (24L * 3600L);
#endif
#endif
#if defined(XP_MAC)
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);
/*
*
*
*
*
*
*
*
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 */
};
1000000L
1000L
101
40001
1000
101