Propagating numerous fixes from js/ref

and development branches, including but
not limited to:

- Preliminary exception handling per
ECMA proposal; try, multiple
catchblocks, and finally.  Catchblocks
are of the form catch (v) or
catch(v:<guard>), where guard is an
optional boolean expression that is
evaluated to determine whether the
exception is to be caught by that block.

- ECMA-proposed 'in' operator; "'foo' in
o" or "4 in o" asks if o has property
foo or element 4.

- Added a new set of defines in
jsconfig.h for js 1.4
features-in-progress.  (in, instanceof,
exception handling.)  Default build
version is now 1.4.  Fixed a few
conditional features that had become
broken.

- Progress towards porting to FreeBSD
and Alpha; casts of NaN and friends to
int are a little more localized.  Not
there yet...

- New config files to compile on more
OSes; various fixes to improve
portability.


git-svn-id: svn://10.0.0.236/trunk@6907 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
mccabe%netscape.com 1998-07-31 00:07:22 +00:00
parent 8eb80556d6
commit b32b6c20e8
35 changed files with 1749 additions and 775 deletions

View File

@ -46,6 +46,10 @@
#include "jsscope.h"
#include "jsscript.h"
#ifdef PERLCONNECT
#include "jsperl.h"
#endif
#ifdef LIVECONNECT
#include "jsjava.h"
#endif
@ -55,6 +59,9 @@
#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
@ -66,6 +73,28 @@
#ifdef XP_MAC
#define isatty(f) 1
#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, malloc(BUFSIZ), _IOLBF, BUFSIZ);
*argc = 1;
*argv = mac_argv;
}
#endif
#ifndef JSFILE
@ -109,7 +138,7 @@ Process(JSContext *cx, JSObject *obj, char *filename)
while((ch = fgetc(ts->file)) != EOF) {
if(ch == '\n' || ch == '\r')
break;
}
}
}
ungetc(ch, ts->file);
}
@ -151,6 +180,82 @@ out:
PR_FreeArenaPool(&cx->tempPool);
}
static int
usage(void)
{
fprintf(stderr, "%s\n", JS_GetImplementationVersion());
fprintf(stderr, "usage: js [-v version] [-f scriptfile] [scriptfile] [scriptarg...]\n");
return 2;
}
static int
ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc)
{
int i;
char *filename = NULL;
jsint length;
jsval *vector;
jsval *p;
JSObject *argsObj;
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, atoi(argv[i+1]));
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);
i++;
break;
default:
return usage();
}
} else {
filename = argv[i++];
break;
}
}
length = argc - i;
vector = JS_malloc(cx, length * sizeof(jsval));
p = vector;
if (vector == NULL)
return 1;
while (i < argc) {
JSString *str = JS_NewStringCopyZ(cx, argv[i]);
if (str == NULL)
return 1;
*p++ = STRING_TO_JSVAL(str);
i++;
}
argsObj = JS_NewArrayObject(cx, length, vector);
if (argsObj == NULL)
return 1;
if (!JS_DefineProperty(cx, obj, "arguments",
OBJECT_TO_JSVAL(argsObj), NULL, NULL, 0))
return 1;
Process(cx, obj, filename);
return 0;
}
static JSBool
Version(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
@ -161,6 +266,9 @@ Version(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
return JS_TRUE;
}
static void
my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report);
static JSBool
Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
@ -179,13 +287,8 @@ Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
filename = JS_GetStringBytes(str);
errno = 0;
script = JS_CompileFile(cx, obj, filename);
if (!script) {
fprintf(stderr, "js: cannot load %s", filename);
if (errno)
fprintf(stderr, ": %s", strerror(errno));
putc('\n', stderr);
if (!script)
continue;
}
ok = JS_ExecuteScript(cx, obj, script, &result);
JS_DestroyScript(cx, script);
if (!ok)
@ -391,7 +494,7 @@ PCToLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
#ifdef DEBUG
static void
SingleNote(JSContext *cx, JSFunction *fun )
SrcNotes(JSContext *cx, JSFunction *fun )
{
uintN offset, delta;
jssrcnote *notes, *sn;
@ -432,6 +535,11 @@ SingleNote(JSContext *cx, JSFunction *fun )
atom = js_GetAtom(cx, &fun->script->atomMap, atomIndex);
printf(" atom %u (%s)", (uintN)atomIndex, ATOM_BYTES(atom));
break;
case SRC_CATCH:
delta = (uintN) js_GetSrcNoteOffset(sn, 0);
if (delta)
printf(" guard size %u", delta);
break;
default:;
}
putchar('\n');
@ -450,23 +558,23 @@ Notes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
if (!fun)
return JS_FALSE;
SingleNote(cx, fun);
SrcNotes(cx, fun);
}
return JS_TRUE;
}
static JSBool
ExceptionTable(JSContext *cx, JSFunction *fun)
TryNotes(JSContext *cx, JSFunction *fun)
{
JSTryNote *iter = fun->script->trynotes;
JSTryNote *tn = fun->script->trynotes;
if (!iter)
if (!tn)
return JS_TRUE;
printf("\nException table:\nstart\tend\tcatch\tfinally\n");
while (iter->start && iter->end) {
printf(" %d\t%d\t%d\t%d\n",
iter->start, iter->end, iter->catch, iter->finally);
iter++;
printf("\nException table:\nstart\tend\tcatch\n");
while (tn->start && tn->catchStart) {
printf(" %d\t%d\t%d\n",
tn->start, tn->length, tn->catchStart);
tn++;
}
return JS_TRUE;
}
@ -492,8 +600,8 @@ Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
return JS_FALSE;
js_Disassemble(cx, fun->script, lines, stdout);
SingleNote(cx, fun);
ExceptionTable(cx, fun);
SrcNotes(cx, fun);
TryNotes(cx, fun);
}
return JS_TRUE;
}
@ -857,6 +965,7 @@ Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
JSString *str;
const char *bytes;
printf("%s\n", JS_GetImplementationVersion());
if (argc == 0) {
ShowHelpHeader();
for (i = 0; shell_functions[i].name; i++)
@ -1046,7 +1155,7 @@ my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
fputs("^\n", stderr);
}
#if defined DEBUG && defined XP_UNIX
#if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
static JSBool
Exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
@ -1099,7 +1208,7 @@ Exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
static JSBool
global_resolve(JSContext *cx, JSObject *obj, jsval id)
{
#if defined DEBUG && defined XP_UNIX
#if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
/*
* Do this expensive hack only for unoptimized Unix builds, which are not
* used for benchmarking.
@ -1155,11 +1264,11 @@ static JSClass global_class = {
int
main(int argc, char **argv)
{
int c, i;
JSVersion version;
JSRuntime *rt;
JSContext *cx;
JSObject *glob, *it;
int result;
#ifdef XP_OS2
/* these streams are normally line buffered on OS/2 and need a \n, *
@ -1168,26 +1277,15 @@ main(int argc, char **argv)
setbuf(stderr,0);
#endif
#ifdef XP_MAC
initConsole("\pJavaScript Shell", "Welcome to js shell.", &argc, &argv);
#endif
version = JSVERSION_DEFAULT;
#ifdef XP_UNIX
while ((c = getopt(argc, argv, "v:")) != -1) {
switch (c) {
case 'v':
version = atoi(optarg);
break;
default:
fprintf(stderr, "usage: js [-v version]\n");
return 2;
}
}
argc -= optind;
argv += optind;
#else
c = -1;
#ifndef LIVECONNECT
argc--;
argv++;
#endif
rt = JS_NewRuntime(8L * 1024L * 1024L);
if (!rt)
return 1;
@ -1214,6 +1312,11 @@ main(int argc, char **argv)
if (!JS_DefineProperties(cx, it, its_props))
return 1;
#ifdef PERLCONNECT
if (!js_InitPerlClass(cx, glob))
return 1;
#endif
#ifdef LIVECONNECT
if (!JSJ_SimpleInit(cx, glob, NULL, getenv("CLASSPATH")))
return 1;
@ -1240,14 +1343,12 @@ main(int argc, char **argv)
* 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 */
if (argc > 0) {
for (i = 0; i < argc; i++)
Process(cx, glob, argv[i]);
} else {
Process(cx, glob, NULL);
}
result = ProcessArgs(cx, glob, argv, argc);
#ifdef JSDEBUGGER
if (_jsdc)
@ -1257,5 +1358,5 @@ main(int argc, char **argv)
JS_DestroyContext(cx);
JS_DestroyRuntime(rt);
JS_ShutDown();
return 0;
return result;
}

View File

@ -326,6 +326,8 @@ JS_TypeOfValue(JSContext *cx, jsval v)
{
JSType type;
JSObject *obj;
JSObjectOps *ops;
JSClass *clasp;
CHECK_REQUEST(cx);
if (JSVAL_IS_VOID(v)) {
@ -333,9 +335,11 @@ JS_TypeOfValue(JSContext *cx, jsval v)
} else if (JSVAL_IS_OBJECT(v)) {
obj = JSVAL_TO_OBJECT(v);
if (obj &&
(OBJ_IS_NATIVE(obj)
? OBJ_GET_CLASS(cx, obj)->call || OBJ_GET_CLASS(cx, obj) == &js_FunctionClass
: obj->map->ops->call != 0)) {
(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;
@ -436,7 +440,7 @@ JS_BeginRequest(JSContext *cx)
JS_LOCK_GC(rt);
while (rt->gcLevel > 0)
JS_AWAIT_GC_DONE(rt);
/* Indicate that a request is running. */
rt->requestCount++;
JS_UNLOCK_GC(rt);
@ -466,7 +470,7 @@ JS_PUBLIC_API(void)
JS_YieldRequest(JSContext *cx)
{
JSRuntime *rt;
CHECK_REQUEST(cx);
PR_ASSERT(rt->requestCount > 0);
@ -506,7 +510,7 @@ JS_ResumeRequest(JSContext *cx)
JS_LOCK_GC(rt);
while (rt->gcLevel > 0)
JS_AWAIT_GC_DONE(rt);
/* Indicate that a request is running. */
rt->requestCount++;
JS_UNLOCK_GC(rt);
@ -543,13 +547,13 @@ JS_DestroyContext(JSContext *cx)
JS_PUBLIC_API(void*)
JS_GetContextPrivate(JSContext *cx)
{
return cx->pvt;
return cx->data;
}
JS_PUBLIC_API(void)
JS_SetContextPrivate(JSContext *cx, void *pvt)
JS_SetContextPrivate(JSContext *cx, void *data)
{
cx->pvt = pvt;
cx->data = data;
}
JS_PUBLIC_API(JSRuntime *)
@ -597,6 +601,13 @@ JS_SetVersion(JSContext *cx, JSVersion version)
return oldVersion;
}
JS_PUBLIC_API(const char *)
JS_GetImplementationVersion(void)
{
return "JavaScript-C 1.3 1998 06 30";
}
JS_PUBLIC_API(JSObject *)
JS_GetGlobalObject(JSContext *cx)
{
@ -672,8 +683,8 @@ JS_malloc(JSContext *cx, size_t nbytes)
{
void *p;
#ifdef XP_OS2
if (nbytes == 0) /*DSR072897 - Windows allows this, OS/2 doesn't*/
#if defined(XP_OS2) || defined(XP_MAC)
if (nbytes == 0) /*DSR072897 - Windows allows this, OS/2 & Mac don't*/
nbytes = 1;
#endif
p = malloc(nbytes);
@ -962,7 +973,7 @@ JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto,
/* Bootstrap Function.prototype (see also JS_InitStandardClasses). */
if (OBJ_GET_CLASS(cx, ctor) == clasp) {
/* XXXMLM - this fails in framesets that are writing over
* themselves!
* themselves!
* PR_ASSERT(!OBJ_GET_PROTO(cx, ctor));
*/
OBJ_SET_PROTO(cx, ctor, proto);
@ -1198,19 +1209,11 @@ JS_DefineConstDoubles(JSContext *cx, JSObject *obj, JSConstDoubleSpec *cds)
* so we don't need to GC-alloc constant doubles.
*/
jsdouble d = cds->dval;
jsint i;
/* We can't do a (jsint) cast to check against JSDOUBLE_IS_INT until we
* know that d is not NaN, or we risk a FPE on some platforms.
*/
if (JSDOUBLE_IS_NaN(d)) {
value = DOUBLE_TO_JSVAL(&cds->dval);
} else {
jsint i = (jsint)d;
value = (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i))
? INT_TO_JSVAL(i)
: DOUBLE_TO_JSVAL(&cds->dval);
}
value = (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i))
? INT_TO_JSVAL(i)
: DOUBLE_TO_JSVAL(&cds->dval);
#else
ok = js_NewNumberValue(cx, cds->dval, &value);
if (!ok)
@ -1754,7 +1757,7 @@ JS_Enumerate(JSContext *cx, JSObject *obj)
vector[i++] = id;
}
}
return ida;
error:
@ -1780,9 +1783,13 @@ JS_NewFunction(JSContext *cx, JSNative call, uintN nargs, uintN flags,
JSAtom *atom;
CHECK_REQUEST(cx);
atom = js_Atomize(cx, name, strlen(name), 0);
if (!atom)
return NULL;
atom = NULL;
if (name) {
atom = js_Atomize(cx, name, strlen(name), 0);
if (!atom)
return NULL;
}
return js_NewFunction(cx, NULL, call, nargs, flags, parent, atom);
}
@ -2529,7 +2536,7 @@ JS_IsExceptionPending(JSContext *cx)
{
CHECK_REQUEST(cx);
#if JS_HAS_EXCEPTIONS
return (JSBool) cx->fp->exceptPending;
return (JSBool) cx->fp->throwing;
#else
return JS_FALSE;
#endif
@ -2540,7 +2547,7 @@ JS_GetPendingException(JSContext *cx, jsval *vp)
{
CHECK_REQUEST(cx);
#if JS_HAS_EXCEPTIONS
if (!cx->fp->exceptPending)
if (!cx->fp->throwing)
return JS_FALSE;
*vp = cx->fp->exception;
return JS_TRUE;
@ -2554,7 +2561,7 @@ JS_SetPendingException(JSContext *cx, jsval v)
{
CHECK_REQUEST(cx);
#if JS_HAS_EXCEPTIONS
cx->fp->exceptPending = JS_TRUE;
cx->fp->throwing = JS_TRUE;
cx->fp->exception = v;
#endif
}
@ -2564,7 +2571,7 @@ JS_ClearPendingException(JSContext *cx)
{
CHECK_REQUEST(cx);
#if JS_HAS_EXCEPTIONS
cx->fp->exceptPending = JS_FALSE;
cx->fp->throwing = JS_FALSE;
#endif
}
@ -2591,7 +2598,6 @@ JS_ClearContextThread(JSContext *cx)
return old;
}
#endif
/************************************************************************/
@ -2649,3 +2655,4 @@ BOOL CALLBACK __loadds WEP(BOOL fSystemExit)
#endif /* !_WIN32 */
#endif /* XP_OS2 */
#endif /* XP_PC */

View File

@ -273,7 +273,7 @@ PR_EXTERN(void*)
JS_GetContextPrivate(JSContext *cx);
PR_EXTERN(void)
JS_SetContextPrivate(JSContext *cx, void *pvt);
JS_SetContextPrivate(JSContext *cx, void *data);
extern JS_PUBLIC_API(JSRuntime *)
JS_GetRuntime(JSContext *cx);
@ -287,6 +287,9 @@ JS_GetVersion(JSContext *cx);
extern JS_PUBLIC_API(JSVersion)
JS_SetVersion(JSContext *cx, JSVersion version);
extern JS_PUBLIC_API(const char *)
JS_GetImplementationVersion(void);
extern JS_PUBLIC_API(JSObject *)
JS_GetGlobalObject(JSContext *cx);
@ -556,7 +559,6 @@ JS_DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value,
* If the object does not have a property by that name, *foundp will be
* JS_FALSE and the value of *attrsp is undefined.
*/
extern JS_PUBLIC_API(JSBool)
JS_GetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name,
uintN *attrsp, JSBool *foundp);
@ -567,7 +569,6 @@ JS_GetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name,
* If the object does not have a property by that name, *foundp will be
* JS_FALSE and nothing will be altered.
*/
extern JS_PUBLIC_API(JSBool)
JS_SetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name,
uintN attrs, JSBool *foundp);
@ -962,7 +963,7 @@ JS_ClearPendingException(JSContext *cx);
#ifdef JS_THREADSAFE
/*
* Associate the current thread with the given context. This is done
* 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

View File

@ -38,30 +38,34 @@
#include "jsobj.h"
#include "jsstr.h"
/* 2^32 - 1 as a number and a string */
#define MAXINDEX 4294967295u
#define MAXSTR "4294967295"
/* Determine if the id represents an array index.
*
/*
* Determine if the id represents an array index.
*
* An id is an array index according to ECMA by (15.4):
*
* "Array objects give special treatment to a certain class of property names.
* A property name P (in the form of a string value) is an array index if and
* only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal
* 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
* 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
* 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)
{
jsuint i;
JSString *str;
jschar *ep;
jschar *cp;
if (JSVAL_IS_INT(id)) {
jsint i;
i = JSVAL_TO_INT(id);
if (i < 0)
return JS_FALSE;
@ -71,21 +75,31 @@ IdIsIndex(jsid id, jsuint *indexp)
/* It must be a string. */
str = JSVAL_TO_STRING(id);
ep = str->chars;
if (str->length == 0 || *ep < '1' || *ep > '9')
return JS_FALSE;
i = 0;
while (*ep != 0 && JS7_ISDEC(*ep)) {
jsuint i2 = i*10 + (*ep - '0');
if (i2 < i)
return JS_FALSE; /* overflow */
i = i2;
ep++;
cp = str->chars;
if (JS7_ISDEC(*cp) && str->length < sizeof(MAXSTR)) {
jsuint index = JS7_UNDEC(*cp++);
jsuint oldIndex = 0;
jsint c;
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;
}
}
if (*ep != 0 || i == 4294967295) /* 4294967295 == 2^32-1 */
return JS_FALSE;
*indexp = i;
return JS_TRUE;
return JS_FALSE;
}
@ -93,9 +107,9 @@ static JSBool
ValueIsLength(JSContext *cx, jsval v, jsuint *lengthp)
{
jsint i;
JSBool res = JS_FALSE;
/* It's only an array length if it's an int or a double. Some relevant
/*
* It's only an array length if it's an int or a double. Some relevant
* ECMA language is 15.4.2.2 - 'If the argument len is a number, then the
* length property of the newly constructed object is set to ToUint32(len)
* - I take 'is a number' to mean 'typeof len' returns 'number' in
@ -106,16 +120,18 @@ ValueIsLength(JSContext *cx, jsval v, jsuint *lengthp)
/* jsuint cast does ToUint32 */
if (lengthp)
*lengthp = (jsuint) i;
res = JS_TRUE;
} else if (JSVAL_IS_DOUBLE(v)) {
/* XXXmccabe I'd love to add another check here, against
return JS_TRUE;
}
if (JSVAL_IS_DOUBLE(v)) {
/*
* XXXmccabe I'd love to add another check here, against
* js_DoubleToInteger(d) != d), so that ValueIsLength matches
* IdIsIndex, but it doesn't seem to follow from ECMA.
* (seems to be appropriate for IdIsIndex, though).
*/
res = js_ValueToECMAUint32(cx, v, (uint32 *)lengthp);
return js_ValueToECMAUint32(cx, v, (uint32 *)lengthp);
}
return res;
return JS_FALSE;
}

View File

@ -115,8 +115,18 @@ js_compare_atom_keys(const void *k1, const void *k2)
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))
return *JSVAL_TO_DOUBLE(v1) == *JSVAL_TO_DOUBLE(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;
}

View File

@ -60,10 +60,10 @@ struct JSRuntime {
/* Random number generator state, used by jsmath.c. */
JSBool rngInitialized;
PRInt64 rngMultiplier;
PRInt64 rngAddend;
PRInt64 rngMask;
PRInt64 rngSeed;
int64 rngMultiplier;
int64 rngAddend;
int64 rngMask;
int64 rngSeed;
jsdouble rngDscale;
/* Well-known numbers held for use by this runtime's contexts. */
@ -84,6 +84,8 @@ struct JSRuntime {
void *newScriptHookData;
JSDestroyScriptHook destroyScriptHook;
void *destroyScriptHookData;
JSTrapHandler debuggerHandler;
void *debuggerHandlerData;
/* More debugging state, see jsdbgapi.c. */
PRCList trapList;
@ -150,7 +152,7 @@ struct JSContext {
JSErrorReporter errorReporter;
/* Client opaque pointer */
void *pvt;
void *data;
/* Java environment and JS errors to throw as exceptions. */
void *javaEnv;
@ -161,6 +163,7 @@ struct JSContext {
JSPackedBool gcActive;
jsrefcount requestDepth;
#endif
JSStackFrame *dormantFrameChain; /* dormant frame chains */
};
typedef struct JSInterpreterHooks {

View File

@ -20,7 +20,7 @@
* JS configuration macros.
*/
#ifndef JS_VERSION
#define JS_VERSION 130
#define JS_VERSION 140
#endif
#if JS_VERSION == 100
@ -67,6 +67,7 @@
#define JS_HAS_IN_OPERATOR 0 /* has in operator ('p' in {p:1}) */
#define JS_HAS_INSTANCEOF 0 /* has {p:1} instanceof Object */
#define JS_HAS_ARGS_OBJECT 0 /* has minimal ECMA arguments object */
#define JS_HAS_DEBUGGER_KEYWORD 0 /* has hook for debugger keyword */
#elif JS_VERSION == 110
@ -112,6 +113,7 @@
#define JS_HAS_IN_OPERATOR 0 /* has in operator ('p' in {p:1}) */
#define JS_HAS_INSTANCEOF 0 /* has {p:1} instanceof Object */
#define JS_HAS_ARGS_OBJECT 0 /* has minimal ECMA arguments object */
#define JS_HAS_DEBUGGER_KEYWORD 0 /* has hook for debugger keyword */
#elif JS_VERSION == 120
@ -157,6 +159,7 @@
#define JS_HAS_IN_OPERATOR 0 /* has in operator ('p' in {p:1}) */
#define JS_HAS_INSTANCEOF 0 /* has {p:1} instanceof Object */
#define JS_HAS_ARGS_OBJECT 0 /* has minimal ECMA arguments object */
#define JS_HAS_DEBUGGER_KEYWORD 0 /* has hook for debugger keyword */
#elif JS_VERSION == 130
@ -202,6 +205,53 @@
#define JS_HAS_IN_OPERATOR 0 /* has in operator ('p' in {p:1}) */
#define JS_HAS_INSTANCEOF 0 /* has {p:1} instanceof Object */
#define JS_HAS_ARGS_OBJECT 1 /* has minimal ECMA arguments object */
#define JS_HAS_DEBUGGER_KEYWORD 1 /* has hook for debugger keyword */
#elif JS_VERSION == 140
#define JS_BUG_AUTO_INDEX_PROPS 0 /* new object o: o.p = v sets o[0] */
#define JS_BUG_NULL_INDEX_PROPS 0 /* o[0] defaults to null, not void */
#define JS_BUG_EMPTY_INDEX_ZERO 0 /* o[""] is equivalent to o[0] */
#define JS_BUG_SHORT_CIRCUIT 0 /* 1 && 1 => true, 1 && 0 => 0 bug */
#define JS_BUG_EAGER_TOSTRING 0 /* o.toString() trumps o.valueOf() */
#define JS_BUG_VOID_TOSTRING 0 /* void 0 + 0 == "undefined0" */
#define JS_BUG_EVAL_THIS_FUN 0 /* eval('this') in function f is f */
#define JS_BUG_EVAL_THIS_SCOPE 0 /* Math.eval('sin(x)') vs. local x */
#define JS_BUG_FALLIBLE_EQOPS 0 /* fallible/intransitive equality ops */
#define JS_BUG_FALLIBLE_TONUM 0 /* fallible ValueToNumber primitive */
#define JS_BUG_WITH_CLOSURE 1 /* with(o)function f(){} sets o.f */
#define JS_BUG_SET_ENUMERATE 0 /* o.p=q flags o.p JSPROP_ENUMERATE */
#define JS_HAS_PROP_DELETE 1 /* delete o.p removes p from o */
#define JS_HAS_CALL_OBJECT 1 /* fun.caller is stack frame obj */
#define JS_HAS_LABEL_STATEMENT 1 /* has break/continue to label: */
#define JS_HAS_DO_WHILE_LOOP 1 /* has do {...} while (b) */
#define JS_HAS_SWITCH_STATEMENT 1 /* has switch (v) {case c: ...} */
#define JS_HAS_SOME_PERL_FUN 1 /* has array.join/reverse/sort */
#define JS_HAS_MORE_PERL_FUN 1 /* has array.push, str.substr, etc */
#define JS_HAS_VALUEOF_HINT 1 /* valueOf(hint) where hint is typeof */
#define JS_HAS_LEXICAL_CLOSURE 1 /* nested functions, lexically closed */
#define JS_HAS_APPLY_FUNCTION 1 /* has apply(fun, arg1, ... argN) */
#define JS_HAS_CALL_FUNCTION 1 /* has fun.call(obj, arg1, ... argN) */
#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */
#define JS_HAS_REGEXPS 1 /* has perl r.e.s via RegExp, /pat/ */
#define JS_HAS_SEQUENCE_OPS 1 /* has array.slice, string.concat */
#define JS_HAS_INITIALIZERS 1 /* has var o = {'foo': 42, 'bar':3} */
#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */
#define JS_HAS_EXPORT_IMPORT 1 /* has export fun; import obj.fun */
#define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */
#define JS_HAS_TRIPLE_EQOPS 1 /* has === and !== identity eqops */
#define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */
#define JS_HAS_REPLACE_LAMBDA 1 /* has string.replace(re, lambda) */
#define JS_HAS_SCRIPT_OBJECT 1 /* has (new Script("x++")).exec() */
#define JS_HAS_XDR 1 /* has XDR API and object methods */
#define JS_HAS_EXCEPTIONS 1 /* has exception handling */
#define JS_HAS_UNDEFINED 1 /* has global "undefined" property */
#define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */
#define JS_HAS_IN_OPERATOR 1 /* has in operator ('p' in {p:1}) */
#define JS_HAS_INSTANCEOF 1 /* has {p:1} instanceof Object */
#define JS_HAS_ARGS_OBJECT 1 /* has minimal ECMA arguments object */
#define JS_HAS_DEBUGGER_KEYWORD 1 /* has hook for debugger keyword */
#else

View File

@ -305,9 +305,9 @@ static jsdouble LocalTZA;
static jsdouble
DaylightSavingTA(jsdouble t)
{
PRInt64 PR_t;
PRInt64 ms2us;
PRInt64 offset;
int64 PR_t;
int64 ms2us;
int64 offset;
jsdouble result;
/* abort if NaN */
@ -1694,7 +1694,7 @@ Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
/* Date called as function */
if (!cx->fp->constructing) {
PRInt64 us, ms, us2ms;
int64 us, ms, us2ms;
jsdouble time;
/* NSPR 2.0 docs say 'We do not support PRMJ_NowMS and PRMJ_NowS',
@ -1710,7 +1710,7 @@ Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
/* Date called as constructor */
if (argc == 0) {
PRInt64 us, ms, us2ms;
int64 us, ms, us2ms;
jsdouble time;
date = date_constructor(cx, obj);
@ -2020,3 +2020,4 @@ js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds)
msFromTime(local));
*date = UTC(local);
}

View File

@ -757,3 +757,13 @@ JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda)
}
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;
}

View File

@ -211,6 +211,11 @@ JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda);
extern JS_PUBLIC_API(void)
JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda);
/************************************************************************/
extern JS_PUBLIC_API(JSBool)
JS_SetDebuggerHandler(JSRuntime *rt, JSTrapHandler handler, void *closure);
PR_END_EXTERN_C
#endif /* jsdbgapi_h___ */

View File

@ -235,10 +235,18 @@ EmitGoto(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
{
JSStmtInfo *stmt;
intN index;
uint16 finallyIndex = 0;
ptrdiff_t offset, delta;
for (stmt = cg->treeContext.topStmt; stmt != toStmt; stmt = stmt->down) {
switch (stmt->type) {
case STMT_FINALLY:
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
js_Emit3(cx, cg, JSOP_JSR, JUMP_OFFSET_HI(finallyIndex),
JUMP_OFFSET_LO(finallyIndex)) < 0)
return -1;
finallyIndex--;
break;
case STMT_WITH:
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
return -1;
@ -401,7 +409,6 @@ EmitNumberOp(JSContext *cx, jsdouble dval, JSCodeGenerator *cg)
JSAtom *atom;
JSAtomListElement *ale;
ival = (jsint)dval;
if (JSDOUBLE_IS_INT(dval, ival) && INT_FITS_IN_JSVAL(ival)) {
if (ival == 0)
return js_Emit1(cx, cg, JSOP_ZERO) >= 0;
@ -439,6 +446,57 @@ js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body,
return JS_TRUE;
}
#if JS_HAS_EXCEPTIONS
#define BYTECODE_ITER(pc, max, body) \
while (pc < max) { \
JSCodeSpec *cs = &js_CodeSpec[(JSOp)*pc]; \
body; \
if ((cs->format & JOF_TYPEMASK) == JOF_TABLESWITCH || \
(cs->format & JOF_TYPEMASK) == JOF_LOOKUPSWITCH) { \
pc += GET_JUMP_OFFSET(pc); \
} else { \
pc += cs->length; \
} \
}
static JSBool
FixupFinallyJumps(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t tryStart,
ptrdiff_t finallyIndex)
{
jsbytecode *pc;
pc = cg->base + tryStart;
BYTECODE_ITER(pc, cg->next, \
if (*pc == JSOP_JSR) { \
ptrdiff_t index = GET_JUMP_OFFSET(pc); \
if (index <= 0) { \
if (index == 0) { \
index = finallyIndex - (pc - cg->base); \
} else { \
index++; \
} \
CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, index); \
} \
} \
);
return JS_TRUE;
}
static JSBool
FixupCatchJumps(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t tryStart,
ptrdiff_t postCatch)
{
jsbytecode *pc;
pc = cg->base + tryStart;
BYTECODE_ITER(pc, cg->next, \
if (*pc == JSOP_GOTO && !GET_JUMP_OFFSET(pc)) { \
CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, postCatch - (pc - cg->base)); \
} \
);
return JS_TRUE;
}
#endif
JSBool
js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
{
@ -462,8 +520,12 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
delta = lineno - cg->currentLine;
cg->currentLine = lineno;
if (delta) {
/* If delta requires too many SRC_NEWLINE notes, use SRC_SETLINE. */
if (delta >= (uintN)(2 + (lineno > SN_2BYTE_OFFSET_MASK))) {
/*
* 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 + ((lineno > SN_3BYTE_OFFSET_MASK) << 1))) {
if (js_NewSrcNote2(cx, cg, SRC_SETLINE, (ptrdiff_t)lineno) < 0)
return JS_FALSE;
} else {
@ -479,20 +541,20 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
{
JSFunction *fun;
/* Fold constants and generate code for the function's body. */
pn2 = pn->pn_body;
if (!js_FoldConstants(cx, pn2))
return JS_FALSE;
if (!js_InitCodeGenerator(cx, &cg2, cg->filename,
pn->pn_pos.begin.lineno,
cg->principals)) {
return JS_FALSE;
}
ok = js_FoldConstants(cx, pn->pn_body);
if (ok) {
cg2.treeContext.tryCount = pn->pn_tryCount;
fun = pn->pn_fun;
ok = js_EmitFunctionBody(cx, &cg2, pn->pn_body, fun);
}
js_ResetCodeGenerator(cx, &cg2);
if (!ok)
cg2.treeContext.tryCount = pn->pn_tryCount;
fun = pn->pn_fun;
if (!js_EmitFunctionBody(cx, &cg2, pn2, fun))
return JS_FALSE;
js_ResetCodeGenerator(cx, &cg2);
/* Make the function object a literal in the outer script's pool. */
atom = js_AtomizeObject(cx, fun->object, 0);
@ -1070,23 +1132,46 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
#if JS_HAS_EXCEPTIONS
case TOK_TRY: {
ptrdiff_t start, end, catch;
#if shaver_finally
/* if this try has a finally, push marker onto treeContext */
ptrdiff_t start, end, catchStart, finallyCatch, catchjmp = -1;
JSParseNode *iter = pn;
/* XXX use -(CG_OFFSET + 1) */
#define EMIT_FINALLY_JSR(cx, cg) \
PR_BEGIN_MACRO \
if (!js_Emit3(cx, cg, JSOP_JSR, 0, 0)) \
return JS_FALSE; \
PR_END_MACRO
;
/*
* When a finally block is `active' (STMT_FINALLY on the treeContext),
* non-local jumps result in a JSR being written into the bytecode
* stream for later fixup. The jsr is written with offset 0 for the
* innermost finally, -1 for the next, etc. As the finally fixup code
* runs for each finished try/finally, it will fix the JSRs with
* offset 0 to match the appropriate finally code for its block
* and decrement all others by one.
*
* NOTE: This will cause problems if we use JSRs for something other
* than finally handling in the future. Caveat hacker!
*/
if (pn->pn_kid3)
js_PushStatement(&cg->treeContext, &stmtInto, STMT_FINALLY,
js_PushStatement(&cg->treeContext, &stmtInfo, STMT_FINALLY,
CG_OFFSET(cg));
#endif
/* mark try location for decompilation, then emit try block */
if (js_NewSrcNote(cx, cg, SRC_TRY) < 0 ||
if (js_NewSrcNote2(cx, cg, SRC_TRYFIN, 0) < 0 ||
js_Emit1(cx, cg, JSOP_NOP) < 0)
return JS_FALSE;
start = CG_OFFSET(cg);
if(!js_EmitTree(cx, cg, pn->pn_kid1))
return JS_FALSE;
/* emit (hidden) jump over try and/or finally */
/* emit (hidden) jump over catch and/or finally */
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
return JS_FALSE;
if (pn->pn_kid3)
EMIT_FINALLY_JSR(cx, cg);
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
return JS_FALSE;
jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0);
@ -1094,66 +1179,198 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
return JS_FALSE;
end = CG_OFFSET(cg);
/* if this try has a catch block, emit it */
if (pn->pn_kid2) {
catch = CG_OFFSET(cg);
if (!js_EmitTree(cx, cg, pn->pn_kid2))
catchStart = end;
/*
* The emitted code for a catch block looks like:
*
* [ popscope ] only if 2nd+ catch block
* name Object
* pushobj
* newinit
* exception
* initprop <atom> maybe marked SRC_CATCHGUARD
* enterwith
* [< catchguard code >] if there's a catchguard
* ifeq <offset to next catch block>
* < catch block contents >
* leavewith
* goto <end of catch blocks> non-local; finally applies
*
* If there's no catch block without a catchguard, the last
* <offset to next catch block> points to rethrow code. This
* code will jsr to the finally code if appropriate, and is
* also used for the catch-all trynote for capturing exceptions
* thrown from catch{} blocks.
*/
do {
JSStmtInfo stmtInfo2;
JSParseNode *disc;
ptrdiff_t guardnote;
iter = iter->pn_kid2;
disc = iter->pn_kid1;
if (catchjmp != -1) {
/* fix up and clean up previous catch block */
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, catchjmp);
if ((uintN)++cg->stackDepth > cg->maxStackDepth)
cg->maxStackDepth = cg->stackDepth;
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0)
return JS_FALSE;
}
/* non-zero guardnote is length of catchguard */
guardnote = js_NewSrcNote2(cx, cg, SRC_CATCH, 0);
if (guardnote < 0 ||
js_Emit1(cx, cg, JSOP_NOP) < 0)
return JS_FALSE;
/* construct the scope holder and push it on */
ale = js_IndexAtom(cx, cx->runtime->atomState.ObjectAtom,
&cg->atomList);
if (!ale)
return JS_FALSE;
EMIT_ATOM_INDEX_OP(JSOP_NAME, ale->index);
if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0 ||
js_Emit1(cx, cg, JSOP_NEWINIT) < 0 ||
js_Emit1(cx, cg, JSOP_EXCEPTION) < 0)
return JS_FALSE;
/* setprop <atomIndex> */
ale = js_IndexAtom(cx, disc->pn_atom, &cg->atomList);
if (!ale)
return JS_FALSE;
EMIT_ATOM_INDEX_OP(JSOP_INITPROP, ale->index);
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
js_Emit1(cx, cg, JSOP_ENTERWITH) < 0)
return JS_FALSE;
/* boolean_expr */
if (disc->pn_expr) {
ptrdiff_t guardstart = CG_OFFSET(cg);
if (!js_EmitTree(cx, cg, disc->pn_expr))
return JS_FALSE;
/* ifeq <next block> */
catchjmp = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0);
if (catchjmp < 0)
return JS_FALSE;
if (!js_SetSrcNoteOffset(cx, cg, guardnote, 0,
(ptrdiff_t)CG_OFFSET(cg) -
guardstart))
return JS_FALSE;
}
/* emit catch block */
js_PushStatement(&cg->treeContext, &stmtInfo2, STMT_WITH,
CG_OFFSET(cg));
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;
/* jsr <finally>, if required */
if (pn->pn_kid3)
EMIT_FINALLY_JSR(cx, cg);
/* this will get fixed up to jump to after catch/finally */
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
js_Emit3(cx, cg, JSOP_GOTO, 0, 0) < 0)
return JS_FALSE;
if (!iter->pn_kid2)
break;
} while (iter);
}
/*
* we use a [leavewith],[jsr],rethrow block for rethrowing
* when there's no unguarded catch, and also for
* running finally code while letting an uncaught exception
* pass through
*/
if (pn->pn_kid3 ||
(catchjmp != -1 && iter->pn_kid1->pn_expr)) {
if (catchjmp != -1) {
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, catchjmp);
}
/* last discriminant jumps to rethrow if none match */
if ((uintN)++cg->stackDepth > cg->maxStackDepth)
cg->maxStackDepth = cg->stackDepth;
if (pn->pn_kid2 &&
(js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0))
return JS_FALSE;
if (pn->pn_kid3) {
finallyCatch = CG_OFFSET(cg);
EMIT_FINALLY_JSR(cx, cg);
}
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
js_Emit1(cx, cg, JSOP_EXCEPTION) < 0 ||
js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
js_Emit1(cx, cg, JSOP_THROW) < 0)
return JS_FALSE;
}
#if shaver_finally
/* if we've got a finally, it goes here, and we have to fix up
the jsrs that might have been emitted before non-local jumps */
if (pn->pn_kid3) {
ptrdiff_t finallyIndex = CG_OFFSET(CG);
js_PopStatement(tc);
if (!FixupFinallyJumps(cx, cg, tryStart, finallyIndex))
ptrdiff_t finallyIndex;
finallyIndex = CG_OFFSET(cg);
if (!FixupFinallyJumps(cx, cg, start, finallyIndex))
return JS_FALSE;
if (!js_EmitTree(cx, cg, pn->pn_kid3))
js_PopStatementCG(cx, cg);
if (js_NewSrcNote2(cx, cg, SRC_TRYFIN, 1) < 0 ||
js_Emit1(cx, cg, JSOP_NOP) < 0 ||
!js_EmitTree(cx, cg, pn->pn_kid3) ||
js_Emit1(cx, cg, JSOP_RETSUB) < 0)
return JS_FALSE;
}
#endif
/* come from post-try */
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);
if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 ||
js_Emit1(cx, cg, JSOP_NOP) < 0)
return JS_FALSE;
/* fix up the end-of-try/catch jumps to come here */
if (!FixupCatchJumps(cx, cg, start, CG_OFFSET(cg)))
return JS_FALSE;
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);
/*
* Add the try note last, to let post-order give us the right ordering
* (first to last, inner to outer).
*/
if (!js_NewTryNote(cx, cg, start, end, catch, 0))
if (pn->pn_kid2 &&
!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_NewTryNote(cx, cg, start, finallyCatch-1, finallyCatch))
return JS_FALSE;
break;
}
case TOK_CATCH:
/*
* Catch blocks are very magical. Mainly, the magic involves
* special generation for the declaration/conditional, and
* mucking with the stack depth counter so that the implicit
* push of the exception that happens at runtime doesn't make
* the code generator very confused.
*
* If a catch block has a finally associated with it, the try
* block will already have pushed the appropriate statement info
* onto the tree context, so we don't need to worry about it.
*/
noteIndex = js_NewSrcNote(cx, cg, SRC_CATCH);
if (noteIndex < 0 ||
!js_Emit1(cx, cg, JSOP_NOP))
return JS_FALSE;
/* need to adjust stack depth */
if ((uintN)++cg->stackDepth > cg->maxStackDepth)
cg->maxStackDepth = cg->stackDepth;
if (!js_EmitTree(cx, cg, pn->pn_left) ||
!js_EmitTree(cx, cg, pn->pn_right))
return JS_FALSE;
break;
#endif /* JS_HAS_EXCEPTIONS */
case TOK_VAR:
@ -1788,6 +2005,11 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
case TOK_PRIMARY:
return js_Emit1(cx, cg, pn->pn_op) >= 0;
#if JS_HAS_DEBUGGER_KEYWORD
case TOK_DEBUGGER:
return js_Emit1(cx, cg, JSOP_DEBUGGER) >= 0;
#endif /* JS_HAS_DEBUGGER_KEYWORD */
default:
PR_ASSERT(0);
}
@ -1816,7 +2038,7 @@ JS_FRIEND_DATA(const char *) js_SrcNoteName[] = {
"cont2label",
"switch",
"funcdef",
"try",
"tryfin",
"catch",
"newline",
"setline",
@ -1844,8 +2066,8 @@ uint8 js_SrcNoteArity[] = {
1, /* SRC_CONT2LABEL */
1, /* SRC_SWITCH */
1, /* SRC_FUNCDEF */
0, /* SRC_TRY */
0, /* SRC_CATCH */
1, /* SRC_TRYFIN */
1, /* SRC_CATCHGUARD */
0, /* SRC_NEWLINE */
1, /* SRC_SETLINE */
0 /* SRC_XDELTA */
@ -1982,8 +2204,8 @@ js_SrcNoteLength(jssrcnote *sn)
if (!arity)
return 1;
for (base = sn++; --arity >= 0; sn++) {
if (*sn & SN_2BYTE_OFFSET_FLAG)
sn++;
if (*sn & SN_3BYTE_OFFSET_FLAG)
sn +=2 ;
}
return sn - base;
}
@ -1995,11 +2217,11 @@ js_GetSrcNoteOffset(jssrcnote *sn, uintN which)
PR_ASSERT(SN_TYPE(sn) != SRC_XDELTA);
PR_ASSERT(which < js_SrcNoteArity[SN_TYPE(sn)]);
for (sn++; which; sn++, which--) {
if (*sn & SN_2BYTE_OFFSET_FLAG)
sn++;
if (*sn & SN_3BYTE_OFFSET_FLAG)
sn += 2;
}
if (*sn & SN_2BYTE_OFFSET_FLAG)
return (ptrdiff_t)((*sn & SN_2BYTE_OFFSET_MASK) << 8) | sn[1];
if (*sn & SN_3BYTE_OFFSET_FLAG)
return (ptrdiff_t)((((uint32)(*sn & SN_3BYTE_OFFSET_MASK)) << 16) | (sn[1] << 8) | sn[2]);
return (ptrdiff_t)*sn;
}
@ -2010,7 +2232,7 @@ js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index,
jssrcnote *sn;
ptrdiff_t diff;
if ((size_t)offset >= (size_t)(SN_2BYTE_OFFSET_FLAG << 8)) {
if (offset >= (((ptrdiff_t)SN_3BYTE_OFFSET_FLAG) << 16)) {
ReportStatementTooLarge(cx, cg);
return JS_FALSE;
}
@ -2020,26 +2242,35 @@ js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index,
PR_ASSERT(SN_TYPE(sn) != SRC_XDELTA);
PR_ASSERT(which < js_SrcNoteArity[SN_TYPE(sn)]);
for (sn++; which; sn++, which--) {
if (*sn & SN_2BYTE_OFFSET_FLAG)
sn++;
if (*sn & SN_3BYTE_OFFSET_FLAG)
sn += 2;
}
/* See if the new offset requires two bytes. */
if ((uintN)offset > (uintN)SN_2BYTE_OFFSET_MASK) {
/* Maybe this offset was already set to a two-byte value. */
if (!(*sn & SN_2BYTE_OFFSET_FLAG)) {
/* Losing, need to insert another byte for this offset. */
/* 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 = sn - cg->notes;
if (cg->noteCount++ % SNINCR == 0) {
cg->noteCount += 2;
/*
* 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) % SNINCR <= 1) {
if (!GrowSrcNotes(cx, cg))
return JS_FALSE;
sn = cg->notes + index;
}
diff = cg->noteCount - (index + 2);
}
diff = cg->noteCount - (index + 3);
PR_ASSERT(diff >= 0);
if (diff > 0)
memmove(sn + 2, sn + 1, diff * sizeof(jssrcnote));
memmove(sn + 3, sn + 1, diff * sizeof(jssrcnote));
}
*sn++ = (jssrcnote)(SN_2BYTE_OFFSET_FLAG | (offset >> 8));
*sn++ = (jssrcnote)(SN_3BYTE_OFFSET_FLAG | (offset >> 16));
*sn++ = (jssrcnote)(offset >> 8);
}
*sn = (jssrcnote)offset;
return JS_TRUE;
@ -2078,18 +2309,16 @@ js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg)
JS_FRIEND_API(JSTryNote *)
js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t start,
ptrdiff_t end, ptrdiff_t catch, ptrdiff_t finally)
ptrdiff_t end, ptrdiff_t catchStart)
{
JSTryNote *cur;
JSTryNote *tn;
PR_ASSERT(cg->tryNext < cg->tryLimit);
cur = cg->tryNext;
cg->tryNext++;
cur->start = start;
cur->end = end;
cur->catch = catch;
cur->finally = finally;
return cur;
tn = cg->tryNext++;
tn->start = start;
tn->length = end - start;
tn->catchStart = catchStart;
return tn;
}
JS_FRIEND_API(JSBool)
@ -2112,9 +2341,8 @@ js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg, JSTryNote **tryp)
}
memcpy(final, tmp, count * sizeof(JSTryNote));
final[count].start = 0;
final[count].end = 0;
final[count].catch = 0;
final[count].finally = 0;
final[count].length = CG_OFFSET(cg);
final[count].catchStart = 0;
*tryp = final;
return JS_TRUE;
}

View File

@ -73,6 +73,7 @@ struct JSTreeContext { /* tree context for semantic checks */
#define TCF_IN_FUNCTION 0x01 /* parsing inside function body */
#define TCF_RETURN_EXPR 0x02 /* function has 'return expr;' */
#define TCF_RETURN_VOID 0x04 /* function has 'return;' */
#define TCF_IN_FOR_INIT 0x08 /* parsing init expr of for; exclude 'in' */
#define INIT_TREE_CONTEXT(tc) \
((tc)->flags = 0, (tc)->tryCount = 0, (tc)->topStmt = NULL)
@ -247,7 +248,7 @@ typedef enum JSSrcNoteType {
SRC_ASSIGNOP = 8, /* += or another assign-op follows */
SRC_COND = 9, /* JSOP_IFEQ is from conditional ?: operator */
SRC_PAREN = 10, /* JSOP_NOP generated to mark user parens */
SRC_HIDDEN = 11, /* JSOP_LEAVEWITH for break/continue in with */
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 */
@ -256,8 +257,8 @@ typedef enum JSSrcNoteType {
SRC_CONT2LABEL = 17, /* JSOP_GOTO for 'continue label' with atomid */
SRC_SWITCH = 18, /* JSOP_*SWITCH with offset to end of switch */
SRC_FUNCDEF = 19, /* JSOP_NOP for function f() with atomid */
SRC_TRY = 20, /* JSOP_NOP for beginning of try{} section */
SRC_CATCH = 21, /* beginning of catch block (at conditional) */
SRC_TRYFIN = 20, /* JSOP_NOP for try{} or finally{} section */
SRC_CATCH = 21, /* catch block has guard */
SRC_NEWLINE = 22, /* bytecode follows a source newline */
SRC_SETLINE = 23, /* a file-absolute source line number note */
SRC_XDELTA = 24 /* 24-31 are for extended delta notes */
@ -295,11 +296,11 @@ typedef enum JSSrcNoteType {
/*
* Offset fields follow certain notes and are frequency-encoded: an offset in
* [0,127] consumes one byte, an offset in [128,32767] takes two, and the high
* bit of the first byte is set.
* [0,0x7f] consumes one byte, an offset in [0x80,0x7fffff] takes three, and
* the high bit of the first byte is set.
*/
#define SN_2BYTE_OFFSET_FLAG 0x80
#define SN_2BYTE_OFFSET_MASK 0x7f
#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[];
@ -361,7 +362,7 @@ js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg);
*/
extern JS_FRIEND_API(JSTryNote *)
js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t start,
ptrdiff_t end, ptrdiff_t catch, ptrdiff_t finally);
ptrdiff_t end, ptrdiff_t catchStart);
/*
* Finish generating exception information, and copy it to JS_malloc

View File

@ -282,10 +282,14 @@ js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent,
/* Splice callobj into the scope chain. */
if (!withobj) {
for (obj = fp->scopeChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {
for (obj = fp->scopeChain; obj; obj = parent) {
if (OBJ_GET_CLASS(cx, obj) != &js_WithClass)
break;
withobj = obj;
parent = OBJ_GET_PARENT(cx, obj);
if (parent == funobj) {
withobj = obj;
break;
}
}
}
if (withobj)
@ -432,8 +436,8 @@ call_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
return JS_TRUE;
}
static JSBool
call_getVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
JSBool
js_GetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSStackFrame *fp;
@ -447,8 +451,8 @@ call_getVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
return JS_TRUE;
}
static JSBool
call_setVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
JSBool
js_SetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSStackFrame *fp;
@ -542,8 +546,8 @@ call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
} else if (getter == js_GetLocalVariable) {
vp = fp->vars;
nslots = fp->nvars;
getter = call_getVariable;
setter = call_setVariable;
getter = js_GetCallVariable;
setter = js_SetCallVariable;
}
if (vp) {
slot = (uintN)JSVAL_TO_INT(propid);
@ -765,7 +769,7 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
case ARGS_LENGTH:
if (!JSVERSION_IS_ECMA(cx->version))
*vp = INT_TO_JSVAL((jsint)(fp->fun ? fp->argc : fun->nargs));
*vp = INT_TO_JSVAL((jsint)(fp && fp->fun ? fp->argc : fun->nargs));
else
case FUN_ARITY:
*vp = INT_TO_JSVAL((jsint)fun->nargs);
@ -809,6 +813,39 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
return JS_TRUE;
}
static JSBool
fun_enumProperty(JSContext *cx, JSObject *obj)
{
JSScope *scope;
JSScopeProperty *sprop;
/* Because properties of function objects such as "length" are
* not defined in function_props to avoid interfering with
* unqualified lookups in local scopes (which pass through the
* function object as a stand-in for the call object), we
* must twiddle any of the special properties not to be enumer-
* ated in this callback, rather than simply predefining the
* properties without JSPROP_ENUMERATE.
*/
JS_LOCK_OBJ(cx, obj);
scope = (JSScope *) obj->map;
for (sprop = scope->props; sprop; sprop = sprop->next) {
jsval id = sprop->id;
if (!JSVAL_IS_INT(id)) {
if (id == ATOM_KEY(cx->runtime->atomState.arityAtom) ||
id == ATOM_KEY(cx->runtime->atomState.lengthAtom) ||
id == ATOM_KEY(cx->runtime->atomState.callerAtom) ||
id == ATOM_KEY(cx->runtime->atomState.nameAtom))
{
sprop->attrs &= ~JSPROP_ENUMERATE;
}
}
}
return JS_TRUE;
}
static JSBool
fun_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
@ -1002,10 +1039,10 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp)
}
if (!JS_XDRStringOrNull(xdr, &atomstr) ||
!JS_XDRUint8(xdr, &fun->nargs) ||
!JS_XDRUint8(xdr, &fun->flags) ||
!JS_XDRUint16(xdr, &fun->nargs) ||
!JS_XDRUint16(xdr, &fun->extra) ||
!JS_XDRUint16(xdr, &fun->nvars))
!JS_XDRUint16(xdr, &fun->nvars) ||
!JS_XDRUint8(xdr, &fun->flags))
return JS_FALSE;
/* do arguments and local vars */
@ -1121,7 +1158,7 @@ JSClass js_FunctionClass = {
JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,
JS_PropertyStub, fun_delProperty,
fun_getProperty, fun_setProperty,
JS_EnumerateStub, (JSResolveOp)fun_resolve,
fun_enumProperty, (JSResolveOp)fun_resolve,
fun_convert, fun_finalize,
NULL, NULL,
NULL, NULL,
@ -1400,7 +1437,7 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
JS_free(cx, collected_args);
return JS_FALSE;
}
tt = js_GetToken(cx, ts);
/* The argument string may be empty or contain no tokens. */
if (tt != TOK_EOF) {
@ -1456,7 +1493,7 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
PR_ASSERT(sprop);
sprop->id = INT_TO_JSVAL(fun->nargs++);
OBJ_DROP_PROPERTY(cx, obj, (JSProperty *)sprop);
/* Done with the NAME; get the next token. */
tt = js_GetToken(cx, ts);
/* Stop if we've reached the end of the string. */
@ -1469,7 +1506,7 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
if (tt == TOK_COMMA)
tt = js_GetToken(cx, ts);
}
}
}
/* Clean up. */
JS_free(cx, collected_args);
if (!js_CloseTokenStream(cx, ts))

View File

@ -30,11 +30,11 @@ struct JSFunction {
jsrefcount nrefs; /* number of referencing objects */
JSObject *object; /* back-pointer to GC'ed object header */
JSNative call; /* native method pointer or null */
uint8 nargs; /* minimum number of actual arguments */
uint8 flags; /* bound method and other flags, see jsapi.h */
uint16 nargs; /* minimum number of actual arguments */
uint16 extra; /* number of arg slots for local GC roots */
uint16 nvars; /* number of local variables */
uint16 spare; /* reserved for future use */
uint8 flags; /* bound method and other flags, see jsapi.h */
uint8 spare; /* reserved for future use */
JSAtom *atom; /* name for diagnostics and decompiling */
JSScript *script; /* interpreted bytecode descriptor or null */
};
@ -85,6 +85,12 @@ 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 JSObject *
js_GetArgsObject(JSContext *cx, JSStackFrame *fp);

View File

@ -615,7 +615,7 @@ js_GC(JSContext *cx)
PRArena *a, *ma, *fa, **ap, **fap;
jsval v, *vp, *sp;
pruword begin, end;
JSStackFrame *fp;
JSStackFrame *fp, *chain;
void *mark;
uint8 flags, *flagp;
JSGCThing *thing, *final, **flp, **oflp;
@ -703,8 +703,19 @@ restart:
js_MarkAtomState(&rt->atomState, gc_mark);
iter = NULL;
while ((acx = js_ContextIterator(rt, &iter)) != NULL) {
fp = acx->fp;
if (fp) {
/* Iterate frame chain and dormant chains. Temporarily tack current
* frame onto the head of the dormant list to ease iteration.
*
* (NOTE: see comment on this whole 'dormant' thing in js_Execute)
*/
chain = acx->fp;
if (chain) {
PR_ASSERT(!chain->dormantNext);
chain->dormantNext = acx->dormantFrameChain;
} else {
chain = acx->dormantFrameChain;
}
for (fp=chain; fp; fp = chain = chain->dormantNext) {
sp = fp->sp;
if (sp) {
for (a = acx->stackPool.first.next; a; a = a->next) {
@ -736,6 +747,9 @@ restart:
GC_MARK(rt, fp->sharpArray, "sharp array", NULL);
} while ((fp = fp->down) != NULL);
}
/* cleanup temporary link */
if (acx->fp)
acx->fp->dormantNext = NULL;
GC_MARK(rt, acx->globalObject, "global object", NULL);
GC_MARK(rt, acx->newborn[GCX_OBJECT], "newborn object", NULL);
GC_MARK(rt, acx->newborn[GCX_STRING], "newborn string", NULL);

View File

@ -24,6 +24,11 @@
#include <string.h>
#include <math.h>
#include "prtypes.h"
#ifndef NSPR20
#include "prarena.h"
#else
#include "plarena.h"
#endif
#include "prlog.h"
#include "jsapi.h"
#include "jsarray.h"
@ -139,7 +144,6 @@ static JSClass prop_iterator_class = {
jsint _i; \
jsval _v; \
\
_i = (jsint)d; \
if (JSDOUBLE_IS_INT(d, _i) && INT_FITS_IN_JSVAL(_i)) { \
_v = INT_TO_JSVAL(_i); \
} else { \
@ -390,6 +394,7 @@ js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
}
/*
* Find a function reference and its 'this' object implicit first parameter
* under argc arguments on cx's stack, and call the function. Push missing
@ -417,9 +422,15 @@ js_Invoke(JSContext *cx, uintN argc, JSBool constructing)
fp = cx->fp;
sp = fp->sp;
/* Once vp is set, control must flow through label out2: to return. */
/*
* Set vp to the callable value's stack slot (it's where rval goes).
* Once vp is set, control must flow through label out2: to return.
* Set frame.rval early so native class and object ops can throw and
* return false, causing a goto out2 with ok set to false.
*/
vp = sp - (2 + argc);
v = *vp;
frame.rval = JSVAL_VOID;
/* A callable must be an object reference. */
if (JSVAL_IS_PRIMITIVE(v))
@ -440,11 +451,12 @@ js_Invoke(JSContext *cx, uintN argc, JSBool constructing)
if (!ok)
goto out2;
if (!JSVAL_IS_FUNCTION(cx, v)) {
fun = NULL;
fun = NULL;
script = NULL;
minargs = nvars = 0;
} else {
funobj = JSVAL_TO_OBJECT(v);
parent = OBJ_GET_PARENT(cx, funobj);
fun = JS_GetPrivate(cx, funobj);
if (clasp != &js_ClosureClass) {
@ -478,16 +490,15 @@ have_fun:
thisp = parent;
else
parent = NULL;
}
}
}
/* Initialize a stack frame, except for thisp and scopeChain. */
/* Initialize most of frame, except for thisp and scopeChain. */
frame.callobj = frame.argsobj = NULL;
frame.script = script;
frame.fun = fun;
frame.argc = argc;
frame.argv = sp - argc;
frame.rval = constructing ? OBJECT_TO_JSVAL(thisp) : JSVAL_VOID;
frame.nvars = nvars;
frame.vars = sp;
frame.down = fp;
@ -499,16 +510,21 @@ have_fun:
frame.constructing = constructing;
frame.overrides = 0;
frame.debugging = JS_FALSE;
frame.exceptPending = JS_FALSE;
frame.throwing = JS_FALSE;
frame.dormantNext = NULL;
/* Compute the 'this' parameter and store it in frame. */
if (thisp) {
/* Some objects (e.g., With) delegate this to another object. */
if (thisp && OBJ_GET_CLASS(cx, thisp) != &js_CallClass) {
/* Some objects (e.g., With) delegate 'this' to another object. */
thisp = OBJ_THIS_OBJECT(cx, thisp);
if (!thisp) {
ok = JS_FALSE;
goto out2;
}
/* Default return value for a constructor is the new object. */
if (constructing)
frame.rval = OBJECT_TO_JSVAL(thisp);
} else {
/*
* ECMA requires "the global object", but in the presence of multiple
@ -526,7 +542,16 @@ have_fun:
*
* The alert should display "true".
*/
thisp = parent ? parent : cx->globalObject;
PR_ASSERT(!constructing);
if (parent == NULL) {
parent = cx->globalObject;
} else {
/* walk up to find the top-level object */
JSObject *tmp;
thisp = parent;
while ((tmp = OBJ_GET_PARENT(cx, thisp)) != NULL)
thisp = tmp;
}
}
frame.thisp = thisp;
@ -631,8 +656,8 @@ have_fun:
#if JS_HAS_EXCEPTIONS
/* Native or script returns JS_FALSE on error or uncaught exception */
if (!ok && frame.exceptPending) {
fp->exceptPending = JS_TRUE;
if (!ok && frame.throwing) {
fp->throwing = JS_TRUE;
fp->exception = frame.exception;
}
#endif
@ -660,13 +685,13 @@ out:
ok &= js_PutArgsObject(cx, &frame);
#endif
/* Pop everything off the stack and store the return value. */
/* Pop everything off the stack and restore cx->fp. */
PR_ARENA_RELEASE(&cx->stackPool, mark);
cx->fp = fp;
*vp = frame.rval;
out2:
/* Must save sp so throw code can store an exception at sp[-1]. */
/* Store the return value and restore sp just above it. */
*vp = frame.rval;
fp->sp = vp + 1;
return ok;
@ -754,10 +779,38 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script, JSFunction *fun,
frame.constructing = JS_FALSE;
frame.overrides = 0;
frame.debugging = debugging;
frame.exceptPending = JS_FALSE;
frame.throwing = JS_FALSE;
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, then some of the objects associated
* with the old frame chain (stored here in the C stack as 'oldfp') were
* not rooted and were being collected. This was bad. So, now we
* preserve the links to these 'dormant' frame chains in cx before
* calling js_Interpret and cleanup afterwards. gc walks these dormant
* chains and marks objects in the same way that it marks object in the
* primary cx->fp chain.
*/
if (oldfp && oldfp != down) {
PR_ASSERT(!oldfp->dormantNext);
oldfp->dormantNext = cx->dormantFrameChain;
cx->dormantFrameChain = oldfp;
}
cx->fp = &frame;
ok = js_Interpret(cx, result);
cx->fp = oldfp;
if (oldfp && oldfp != down) {
PR_ASSERT(cx->dormantFrameChain == oldfp);
cx->dormantFrameChain = oldfp->dormantNext;
oldfp->dormantNext = NULL;
}
return ok;
}
@ -850,9 +903,9 @@ ImportProperty(JSContext *cx, JSObject *obj, jsid id)
* properties are accessed by opcodes using stack slot numbers
* generated by the compiler rather than runtime name-lookup. These
* local references, therefore, bypass the normal scope chain lookup.
* So, instead of defining a new property in the activation object,
* So, instead of defining a new property in the activation object,
* modify the existing value in the stack slot.
*/
*/
if (OBJ_GET_CLASS(cx, target) == &js_CallClass) {
ok = OBJ_LOOKUP_PROPERTY(cx, target, id, &obj2, &prop);
if (!ok)
@ -1015,19 +1068,21 @@ js_Interpret(JSContext *cx, jsval *result)
if (rt->interruptHandler) {
JSTrapHandler handler = (JSTrapHandler) rt->interruptHandler;
switch (handler(cx, script, pc, &rval,
rt->interruptHandlerData)) {
case JSTRAP_ERROR:
ok = JS_FALSE;
goto out;
case JSTRAP_CONTINUE:
break;
case JSTRAP_RETURN:
fp->rval = rval;
goto out;
default:;
}
/* check copy of pointer for safety in multithreaded situation */
if (handler) {
switch (handler(cx, script, pc, &rval,
rt->interruptHandlerData)) {
case JSTRAP_ERROR:
ok = JS_FALSE;
goto out;
case JSTRAP_CONTINUE:
break;
case JSTRAP_RETURN:
fp->rval = rval;
goto out;
default:;
}
}
}
switch (op) {
@ -1139,15 +1194,22 @@ js_Interpret(JSContext *cx, jsval *result)
} \
}
#if JS_HAS_IN_OPERATOR
case JSOP_IN:
rval = POP();
VALUE_TO_OBJECT(cx, rval, obj);
if (!JSVAL_IS_OBJECT(rval) || rval == JSVAL_NULL) {
JS_ReportError(cx, "target of 'in' operator must be an object");
ok = JS_FALSE;
goto out;
}
obj = JSVAL_TO_OBJECT(rval);
POP_ELEMENT_ID(id);
ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
if (!ok)
goto out;
PUSH_OPND(BOOLEAN_TO_JSVAL(prop != NULL));
break;
#endif /* JS_HAS_IN_OPERATOR */
case JSOP_FORNAME:
rval = POP();
@ -1220,7 +1282,6 @@ js_Interpret(JSContext *cx, jsval *result)
/* 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, NULL);
if (!propobj) {
@ -1228,7 +1289,7 @@ js_Interpret(JSContext *cx, jsval *result)
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.
@ -1240,13 +1301,12 @@ js_Interpret(JSContext *cx, jsval *result)
if (!ok)
goto out;
/*
/*
* Stash private iteration state into property iterator object.
* NB: This code knows that the first slots are pre-allocated.
*/
PR_ASSERT(JS_INITIAL_NSLOTS >= 5);
propobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj);
} else {
/* This is not the first iteration. Recover iterator state. */
propobj = JSVAL_TO_OBJECT(rval);
@ -1255,14 +1315,12 @@ js_Interpret(JSContext *cx, jsval *result)
}
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_ITR_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) {
@ -1283,7 +1341,6 @@ js_Interpret(JSContext *cx, jsval *result)
/* Skip deleted and shadowed properties, leave next id in rval. */
if (obj != origobj) {
/* Have we already enumerated a clone of this property? */
ok = OBJ_LOOKUP_PROPERTY(cx, origobj, rval, &obj2, &prop);
if (!ok)
@ -1303,7 +1360,8 @@ js_Interpret(JSContext *cx, jsval *result)
/* 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) {
} else if ((cx->version <= JSVERSION_1_1) &&
(cx->version >= JSVERSION_1_0)) {
str = js_NumberToString(cx, (jsdouble) JSVAL_TO_INT(rval));
if (!str) {
ok = JS_FALSE;
@ -1846,10 +1904,9 @@ js_Interpret(JSContext *cx, jsval *result)
if (!ok) {
cx->newborn[GCX_OBJECT] = NULL;
#if JS_HAS_EXCEPTIONS
if (fp->exceptPending) {
/* throw expects to have the exception on the stack */
if (fp->throwing) {
sp[-1] = fp->exception;
goto throw;
goto do_throw;
}
#endif
goto out;
@ -2067,10 +2124,10 @@ js_Interpret(JSContext *cx, jsval *result)
RESTORE_SP(fp);
if (!ok) {
#if JS_HAS_EXCEPTIONS
if (fp->exceptPending) {
/* throw expects to have the exception on the stack */
if (fp->throwing) {
/* do_throw expects to have the exception on the stack */
sp[-1] = fp->exception;
goto throw;
goto do_throw;
}
#endif
goto out;
@ -2438,7 +2495,7 @@ js_Interpret(JSContext *cx, jsval *result)
*vp = sp[-1];
break;
#ifdef JS_HAS_INITIALIZERS
#if JS_HAS_INITIALIZERS
case JSOP_NEWINIT:
argc = 0;
fp->sharpDepth++;
@ -2527,32 +2584,41 @@ js_Interpret(JSContext *cx, jsval *result)
#endif /* JS_HAS_INITIALIZERS */
#if JS_HAS_EXCEPTIONS
case JSOP_JSR:
len = GET_JUMP_OFFSET(pc);
PUSH(INT_TO_JSVAL(pc - script->code));
CHECK_BRANCH(len);
break;
case JSOP_RETSUB:
rval = POP();
PR_ASSERT(JSVAL_IS_INT(rval));
pc = script->code + JSVAL_TO_INT(rval) + 3 /* JSR */;
len = 0;
CHECK_BRANCH(-1);
break;
case JSOP_EXCEPTION:
PUSH(fp->exception);
break;
case JSOP_THROW:
/*
* We enter here with an exception on the stack already, so we
* need to pop it off if we're leaving this frame, but we leave
* it on if we're jumping to a try section.
*/
PR_ASSERT(!fp->exceptPending);
throw:
do_throw:
fp->exception = POP();
tn = script->trynotes;
offset = PTRDIFF(pc, script->code, jsbytecode);
if (tn) {
for (;
tn->start && tn->end && (tn->start > offset || tn->end < offset);
tn++)
/* nop */
;
if (tn->catch) {
pc = script->code + tn->catch;
while (PR_UPTRDIFF(offset, tn->start) >= (pruword)tn->length)
tn++;
if (tn->catchStart) {
pc = script->code + tn->catchStart;
len = 0;
goto advance_pc;
}
}
/* Not in a catch block, so propagate the exception. */
fp->exceptPending = JS_TRUE;
fp->exception = POP();
fp->throwing = JS_TRUE;
ok = JS_FALSE;
goto out;
#endif /* JS_HAS_EXCEPTIONS */
@ -2582,6 +2648,29 @@ js_Interpret(JSContext *cx, jsval *result)
break;
#endif /* JS_HAS_INSTANCEOF */
#if JS_HAS_DEBUGGER_KEYWORD
case JSOP_DEBUGGER:
if (rt->debuggerHandler) {
JSTrapHandler handler = (JSTrapHandler) rt->debuggerHandler;
/* check copy of pointer for safety in multithread situation */
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;
default:;
}
}
}
break;
#endif /* JS_HAS_DEBUGGER_KEYWORD */
default:
JS_ReportError(cx, "unimplemented JavaScript bytecode %d", op);
ok = JS_FALSE;
@ -2612,7 +2701,6 @@ js_Interpret(JSContext *cx, jsval *result)
}
#endif
}
out:
/*
* Restore the previous frame's execution state.

View File

@ -48,8 +48,9 @@ struct JSStackFrame {
JSPackedBool constructing; /* true if called via new operator */
uint8 overrides; /* bit-set of overridden Call properties */
JSPackedBool debugging; /* true if for JS_EvaluateInStackFrame */
JSPackedBool exceptPending; /* is there an uncaught exception? */
jsval exception; /* most-recently-thrown exception */
JSPackedBool throwing; /* is there a pending exception? */
jsval exception; /* most-recently-thrown exceptin */
JSStackFrame *dormantNext; /* next dormant frame chain */
};
/*

View File

@ -34,13 +34,13 @@
static PRLock *_global_lock;
static void
js_LockGlobal()
js_LockGlobal()
{
PR_Lock(_global_lock);
}
static void
js_UnlockGlobal()
js_UnlockGlobal()
{
PR_Unlock(_global_lock);
}
@ -113,8 +113,8 @@ swap [%1],%4
ba 3f
mov 0,%0
2: mov 1,%0
3:"
: "=r" (res)
3:"
: "=r" (res)
: "r" (w), "r" (ov), "r" (nv), "r" (-1));
#else /* ULTRA_SPARC */
PR_ASSERT(ov != nv);
@ -124,8 +124,8 @@ cmp %2,%3
be,a 1f
mov 1,%0
mov 0,%0
1:"
: "=r" (res)
1:"
: "=r" (res)
: "r" (w), "r" (ov), "r" (nv));
#endif /* ULTRA_SPARC */
return (int)res;
@ -135,7 +135,7 @@ mov 0,%0
PR_ASSERT(nv >= 0);
#else
PR_ASSERT(ov != nv);
#endif
#endif
return compare_and_swap(w,ov,nv);
#endif
}
@ -230,7 +230,7 @@ js_DestroyLock(JSThinLock *p)
static void js_Dequeue(JSThinLock *);
PR_INLINE jsval
js_GetSlotWhileLocked(JSContext *cx, JSObject *obj, uint32 slot)
js_GetSlotWhileLocked(JSContext *cx, JSObject *obj, uint32 slot)
{
jsval v;
#ifndef NSPR_LOCK
@ -265,7 +265,7 @@ js_GetSlotWhileLocked(JSContext *cx, JSObject *obj, uint32 slot)
}
PR_INLINE void
js_SetSlotWhileLocked(JSContext *cx, JSObject *obj, uint32 slot, jsval v)
js_SetSlotWhileLocked(JSContext *cx, JSObject *obj, uint32 slot, jsval v)
{
#ifndef NSPR_LOCK
JSScope *scp = (JSScope *)obj->map;
@ -292,14 +292,14 @@ js_SetSlotWhileLocked(JSContext *cx, JSObject *obj, uint32 slot, jsval v)
obj->slots[slot] = v;
return;
}
#endif
#endif
js_LockObj(cx,obj);
obj->slots[slot] = v;
js_UnlockObj(cx,obj);
}
static JSFatLock *
mallocFatlock()
mallocFatlock()
{
JSFatLock *fl = (JSFatLock *)malloc(sizeof(JSFatLock)); /* for now */
PR_ASSERT(fl);
@ -312,14 +312,14 @@ mallocFatlock()
}
static void
freeFatlock(JSFatLock *fl)
freeFatlock(JSFatLock *fl)
{
PR_DestroyLock(fl->slock);
PR_DestroyLock(fl->slock);
PR_DestroyCondVar(fl->svar);
free(fl);
}
static int
static int
js_SuspendThread(JSThinLock *p)
{
JSFatLock *fl = p->fat;
@ -332,7 +332,7 @@ js_SuspendThread(JSThinLock *p)
PR_Unlock(fl->slock);
return 1;
}
stat = PR_WaitCondVar(fl->svar,PR_INTERVAL_NO_TIMEOUT);
stat = PR_WaitCondVar(fl->svar,PR_INTERVAL_NO_TIMEOUT);
if (stat == PR_FAILURE) {
fl->susp--;
return 0;
@ -347,14 +347,14 @@ js_ResumeThread(JSThinLock *p)
JSFatLock *fl = p->fat;
PRStatus stat;
PR_Lock(fl->slock);
fl->susp--;
fl->susp--;
stat = PR_NotifyCondVar(fl->svar);
PR_ASSERT(stat != PR_FAILURE);
PR_Unlock(fl->slock);
}
static JSFatLock *
listOfFatlocks(int l)
static JSFatLock *
listOfFatlocks(int l)
{
JSFatLock *m;
JSFatLock *m0;
@ -370,7 +370,7 @@ listOfFatlocks(int l)
}
static void
deleteListOfFatlocks(JSFatLock *m)
deleteListOfFatlocks(JSFatLock *m)
{
JSFatLock *m0;
for (; m; m=m0) {
@ -382,7 +382,7 @@ deleteListOfFatlocks(JSFatLock *m)
static JSFatLockTable _fl_table;
JSFatLock *
allocateFatlock()
allocateFatlock()
{
JSFatLock *m;
@ -404,8 +404,8 @@ allocateFatlock()
return m;
}
void
deallocateFatlock(JSFatLock *m)
void
deallocateFatlock(JSFatLock *m)
{
if (m == NULL)
return;
@ -489,16 +489,16 @@ js_InitContextForLocking(JSContext *cx)
Using p->slock as below (and correspondingly in js_SuspendThread()),
js_SuspendThread() will notice that p->fat is empty, and hence return
immediately.
immediately.
*/
int
emptyFatlock(JSThinLock *p)
emptyFatlock(JSThinLock *p)
{
JSFatLock *fl = p->fat;
int i;
if (fl == NULL)
return 1;
PR_Lock(fl->slock);
@ -512,7 +512,7 @@ emptyFatlock(JSThinLock *p)
return i < 1;
}
/*
/*
Fast locking and unlocking is implemented by delaying the
allocation of a system lock (fat lock) until contention. As long as
a locking thread A runs uncontended, the lock is represented solely
@ -608,7 +608,7 @@ js_Lock(JSThinLock *p, prword me)
}
PR_INLINE void
js_Unlock(JSThinLock *p, prword me)
js_Unlock(JSThinLock *p, prword me)
{
#ifdef DEBUG_
printf("\nT%x about to unlock %p\n",me,p);
@ -764,3 +764,4 @@ js_IsScopeLocked(JSScope *scope)
#endif
#endif /* JS_THREADSAFE */

View File

@ -233,9 +233,9 @@ math_pow(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
* Math.random() support, lifted from java.util.Random.java.
*/
static void
random_setSeed(JSRuntime *rt, PRInt64 seed)
random_setSeed(JSRuntime *rt, int64 seed)
{
PRInt64 tmp;
int64 tmp;
LL_I2L(tmp, 1000);
LL_DIV(seed, seed, tmp);
@ -246,7 +246,7 @@ random_setSeed(JSRuntime *rt, PRInt64 seed)
static void
random_init(JSRuntime *rt)
{
PRInt64 tmp, tmp2;
int64 tmp, tmp2;
/* Do at most once. */
if (rt->rngInitialized)
@ -277,7 +277,7 @@ random_init(JSRuntime *rt)
static uint32
random_next(JSRuntime *rt, int bits)
{
PRInt64 nextseed, tmp;
int64 nextseed, tmp;
uint32 retval;
LL_MUL(nextseed, rt->rngSeed, rt->rngMultiplier);
@ -292,7 +292,7 @@ random_next(JSRuntime *rt, int bits)
static jsdouble
random_nextDouble(JSRuntime *rt)
{
PRInt64 tmp, tmp2;
int64 tmp, tmp2;
jsdouble d;
LL_ISHL(tmp, random_next(rt, 27), 27);
@ -412,3 +412,4 @@ js_InitMathClass(JSContext *cx, JSObject *obj)
return NULL;
return proto;
}

View File

@ -76,25 +76,22 @@ num_isFinite(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
}
static JSBool
num_parseFloat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval)
num_parseFloat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSString *str;
jsdouble d, *dp;
jschar *ep;
jsdouble d;
const jschar *ep;
str = js_ValueToString(cx, argv[0]);
if (!str)
return JS_FALSE;
if (!js_strtod(str->chars, &ep, &d) || ep == str->chars) {
if (!js_strtod(cx, str->chars, &ep, &d))
return JS_FALSE;
if (ep == str->chars) {
*rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
} else {
dp = js_NewDouble(cx, d);
if (!dp)
return JS_FALSE;
*rval = DOUBLE_TO_JSVAL(dp);
return JS_TRUE;
}
return JS_TRUE;
return js_NewNumberValue(cx, d, rval);
}
/* See ECMA 15.1.2.2. */
@ -103,114 +100,33 @@ num_parseInt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSString *str;
jsint radix;
const jschar *chars, *start;
jsdouble sum;
JSBool negative;
uintN digit, newDigit;
jschar c;
jschar digitMax = '9';
jschar lowerCaseBound = 'a';
jschar upperCaseBound = 'A';
jsdouble d;
const jschar *ep;
str = js_ValueToString(cx, argv[0]);
if (!str)
return JS_FALSE;
chars = str->chars;
/* This assumes that the char strings are null-terminated - JS_ISSPACE will
* always evaluate to false at the end of a string containing only
* whitespace.
*/
while (JS_ISSPACE(*chars))
chars++;
if ((negative = (*chars == '-')) != 0 || *chars == '+')
chars++;
if (argc > 1) {
if (!js_ValueToECMAInt32(cx, argv[1], &radix))
return JS_FALSE;
} else {
} else
radix = 0;
}
if (radix != 0) {
/* Some explicit radix was supplied */
if (radix < 2 || radix > 36) {
*rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
return JS_TRUE;
} else {
if (radix == 16 && *chars == '0' &&
(*(chars + 1) == 'X' || *(chars + 1) == 'x'))
/* If radix is 16, ignore hex prefix. */
chars += 2;
}
} else {
/* No radix supplied, or some radix that evaluated to 0. */
if (*chars == '0') {
/* It's either hex or octal; only increment char if str isn't '0' */
if (*(chars + 1) == 'X' || *(chars + 1) == 'x') /* Hex */
{
chars += 2;
radix = 16;
} else { /* Octal */
radix = 8;
}
} else {
radix = 10; /* Default to decimal. */
}
}
/* Done with the preliminaries; find some prefix of the string that's
* a number in the given radix. XXX might want to farm this out to
* a utility function, to share with the lexer's handling of
* literal constants.
*/
start = chars; /* Mark - if string is empty, we return NaN. */
if (radix < 10) {
digitMax = (char) ('0' + radix - 1);
} else {
digitMax = '9';
}
if (radix > 10) {
lowerCaseBound = (char) ('a' + radix - 10);
upperCaseBound = (char) ('A' + radix - 10);
} else {
lowerCaseBound = 'a';
upperCaseBound = 'A';
}
digit = 0; /* how many digits have we seen? (if radix == 10) */
sum = 0;
while ((c = *chars) != 0) { /* 0 is never a valid character. */
if ('0' <= c && c <= digitMax)
newDigit = c - '0';
else if ('a' <= c && c < lowerCaseBound)
newDigit = c - 'a' + 10;
else if ('A' <= c && c < upperCaseBound)
newDigit = c - 'A' + 10;
else
break;
/* ECMA: 'But if R is 10 and Z contains more than 20 significant
* digits, every digit after the 20th may be replaced by a 0...'
* I'm not sure if doing this way is actually an improvement.
* XXX this may bear closer examination......
*/
if (radix == 10 && ++digit > 20)
sum = sum*radix;
else
sum = sum*radix + newDigit;
chars++;
}
if (chars == start) {
if (radix != 0 && (radix < 2 || radix > 36)) {
*rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
} else {
if (!js_NewNumberValue(cx, (negative ? -sum : sum), rval))
return JS_FALSE;
return JS_TRUE;
}
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);
}
static JSFunctionSpec number_functions[] = {
{"isNaN", num_isNaN, 1},
{"isFinite", num_isFinite, 1},
@ -477,7 +393,6 @@ js_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval)
{
jsint i;
i = (jsint)d;
if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) {
*rval = INT_TO_JSVAL(i);
} else {
@ -511,7 +426,6 @@ js_NumberToString(JSContext *cx, jsdouble d)
jsint i;
char buf[32];
i = (jsint)d;
if (JSDOUBLE_IS_INT(d, i)) {
PR_snprintf(buf, sizeof buf, "%ld", (long)i);
} else {
@ -525,7 +439,7 @@ js_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp)
{
JSObject *obj;
JSString *str;
jschar *ep;
const jschar *ep;
jsdouble d;
if (JSVAL_IS_OBJECT(v)) {
@ -544,8 +458,11 @@ js_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp)
} else if (JSVAL_IS_STRING(v)) {
str = JSVAL_TO_STRING(v);
errno = 0;
if ((!js_strtod(str->chars, &ep, &d) || *ep != 0) &&
(!js_strtol(str->chars, &ep, 0, &d) || *ep != 0)) {
/* Note that ECMAScript doesn't treat numbers beginning with a zero as octal numbers 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, &d) || js_SkipWhiteSpace(ep) != str->chars + str->length) &&
(!js_strtointeger(cx, str->chars, &ep, 0, &d) || js_SkipWhiteSpace(ep) != str->chars + str->length)) {
goto badstr;
}
*dp = d;
@ -695,60 +612,214 @@ js_DoubleToInteger(jsdouble d)
return neg ? -d : d;
}
/* XXXbe rewrite me! */
JSBool
js_strtod(const jschar *s, jschar **ep, jsdouble *dp)
{
size_t i, n;
char *cstr, *estr;
jsdouble d;
n = js_strlen(s);
cstr = malloc(n + 1);
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 = malloc(length + 1);
if (!cstr)
return JS_FALSE;
for (i = 0; i <= n; i++) {
if (s[i] >> 8) {
free(cstr);
return JS_FALSE;
for (i = 0; i <= length; i++) {
if (s1[i] >> 8) {
cstr[i] = 0;
break;
}
cstr[i] = (char)s[i];
cstr[i] = (char)s1[i];
}
errno = 0;
d = PR_strtod(cstr, &estr);
istr = cstr;
if ((negative = (*istr == '-')) != 0 || *istr == '+')
istr++;
if (!strncmp(istr, "Infinity", 8)) {
d = *(negative ? cx->runtime->jsNegativeInfinity : cx->runtime->jsPositiveInfinity);
estr = istr + 8;
} else {
errno = 0;
d = PR_strtod(cstr, &estr);
if (errno == ERANGE)
if (d == HUGE_VAL)
d = *cx->runtime->jsPositiveInfinity;
else if (d == -HUGE_VAL)
d = *cx->runtime->jsNegativeInfinity;
}
free(cstr);
if (errno == ERANGE)
return JS_FALSE;
*ep = (jschar *)s + (estr - cstr);
i = estr - cstr;
*ep = i ? s1 + i : s;
*dp = d;
return JS_TRUE;
}
/* XXXbe rewrite me! */
JSBool
js_strtol(const jschar *s, jschar **ep, jsint base, jsdouble *dp)
struct BinaryDigitReader
{
size_t i, n;
char *cstr, *estr;
long l;
uintN base; /* Base of number; must be a power of 2 */
uintN digit; /* Current digit value in radix given by base */
uintN digitMask; /* Mask to extract the next bit from digit */
const jschar *digits; /* Pointer to the remaining digits */
const jschar *end; /* Pointer to first non-digit */
};
n = js_strlen(s);
cstr = malloc(n + 1);
if (!cstr)
return JS_FALSE;
for (i = 0; i <= n; i++) {
if (s[i] >> 8) {
free(cstr);
return JS_FALSE;
}
cstr[i] = (char)s[i];
/* Return the next binary digit from the number or -1 if done */
static intN GetNextBinaryDigit(struct BinaryDigitReader *bdr)
{
intN bit;
if (bdr->digitMask == 0) {
uintN c;
if (bdr->digits == bdr->end)
return -1;
c = *bdr->digits++;
if ('0' <= c && c <= '9')
bdr->digit = c - '0';
else if ('a' <= c && c <= 'z')
bdr->digit = c - 'a' + 10;
else bdr->digit = c - 'A' + 10;
bdr->digitMask = bdr->base >> 1;
}
bit = (bdr->digit & bdr->digitMask) != 0;
bdr->digitMask >>= 1;
return bit;
}
JSBool
js_strtointeger(JSContext *cx, const jschar *s, const jschar **ep, jsint base, jsdouble *dp)
{
JSBool negative;
jsdouble value;
const jschar *start;
const jschar *s1 = js_SkipWhiteSpace(s);
if ((negative = (*s1 == '-')) != 0 || *s1 == '+')
s1++;
if (base == 0)
/* No base supplied, or some base that evaluated to 0. */
if (*s1 == '0')
/* It's either hex or octal; only increment char if str isn't '0' */
if (s1[1] == 'X' || s1[1] == 'x') { /* Hex */
s1 += 2;
base = 16;
} else /* Octal */
base = 8;
else
base = 10; /* Default to decimal. */
else if (base == 16 && *s1 == '0' && (s1[1] == 'X' || s1[1] == 'x'))
/* If base is 16, ignore hex prefix. */
s1 += 2;
/* Done with the preliminaries; find some prefix of the string that's
* a number in the given base.
*/
start = s1; /* Mark - if string is empty, we return NaN. */
value = 0.0;
while (1) {
uintN digit;
jschar c = *s1;
if ('0' <= c && c <= '9')
digit = c - '0';
else if ('a' <= c && c <= 'z')
digit = c - 'a' + 10;
else if ('A' <= c && c <= 'Z')
digit = c - 'A' + 10;
else
break;
if (digit >= (uintN)base)
break;
value = value*base + digit;
s1++;
}
if (value >= 9007199254740992.0)
if (base == 10) {
/* If we're accumulating a decimal number and the number is >= 2^53, then
* the result from the repeated multiply-add above may be inaccurate. Call
* PR_strtod to get the correct answer.
*/
size_t i;
size_t length = s1 - start;
char *cstr = 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 = PR_strtod(cstr, &estr);
if (errno == ERANGE && value == HUGE_VAL)
value = *cx->runtime->jsPositiveInfinity;
free(cstr);
} else if (base == 2 || base == 4 || base == 8 || base == 16 || base == 32) {
/* The number may also be inaccurate for one of these 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 of this 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;
}
errno = 0;
l = strtol(cstr, &estr, base);
free(cstr);
if (errno == ERANGE)
return JS_FALSE;
*ep = (jschar *)s + (estr - cstr);
*dp = (jsdouble)l;
return JS_TRUE;
}

View File

@ -57,14 +57,14 @@ PR_BEGIN_EXTERN_C
#define JSDOUBLE_IS_NEGZERO(d) (JSDOUBLE_HI32(d) == JSDOUBLE_HI32_SIGNBIT && \
JSDOUBLE_LO32(d) == 0)
#define JSDOUBLE_IS_INT_2(d, i) (!JSDOUBLE_IS_NEGZERO(d) && (jsdouble)i == d)
#ifdef XP_PC
/* XXX MSVC miscompiles NaN floating point comparisons for ==, !=, <, and <= */
#define JSDOUBLE_IS_INT(d, i) (!JSDOUBLE_IS_NaN(d) && JSDOUBLE_IS_INT_2(d, i))
#else
#define JSDOUBLE_IS_INT(d, i) JSDOUBLE_IS_INT_2(d, i)
#endif
/*
* JSDOUBLE_IS_INT first checks that d is neither NaN nor infinite, to avoid
* raising SIGFPE on platforms such as Alpha Linux, then (only if the cast is
* safe) leaves i as (jsint)d. This also avoid anomalous NaN floating point
* comparisons under MSVC.
*/
#define JSDOUBLE_IS_INT(d, i) (JSDOUBLE_IS_FINITE(d) && !JSDOUBLE_IS_NEGZERO(d) \
&& ((d) == (i = (jsint)(d))))
/* Initialize the Number class, returning its prototype object. */
extern JSObject *
@ -140,11 +140,27 @@ js_ValueToUint16(JSContext *cx, jsval v, uint16 *ip);
extern jsdouble
js_DoubleToInteger(jsdouble d);
/*
* Similar to strtod except that replaces overflows with infinities of the correct
* sign and underflows with zeros of the correct sign. Guaranteed to return the
* closest double number to the given input in dp.
* Also allows inputs of the form [+|-]Infinity, which produce an infinity of the
* appropriate sign. The case of the "Infinity" string must match.
* If the string does not have a number in it, set *ep to s and return 0.0 in dp.
* Return false if out of memory.
*/
extern JSBool
js_strtod(const jschar *s, jschar **ep, jsdouble *dp);
js_strtod(JSContext *cx, const jschar *s, const jschar **ep, jsdouble *dp);
/*
* Similar to strtol except that handles integers of arbitrary size. Guaranteed to
* return the closest double number to the given input when radix is 10 or a power of 2.
* May experience roundoff errors for very large numbers of a different radix.
* If the string does not have a number in it, set *ep to s and return 0.0 in dp.
* Return false if out of memory.
*/
extern JSBool
js_strtol(const jschar *s, jschar **ep, jsint radix, jsdouble *dp);
js_strtointeger(JSContext *cx, const jschar *s, const jschar **ep, jsint radix, jsdouble *dp);
PR_END_EXTERN_C

View File

@ -278,7 +278,7 @@ js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap,
hash = js_hash_object(obj);
hep = PR_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
@ -1081,11 +1081,9 @@ FindConstructor(JSContext *cx, const char *name, jsval *vp)
}
}
if(!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, (JSProperty **) &sprop))
{
if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, (JSProperty**)&sprop))
return JS_FALSE;
}
if(!sprop) {
if (!sprop) {
*vp = JSVAL_VOID;
return JS_TRUE;
}
@ -1241,24 +1239,33 @@ js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot)
#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) \
PR_BEGIN_MACRO \
if (!JSVAL_IS_INT(id)) { \
if (!JSVAL_IS_INT(id)) { \
JSAtom *_atom = (JSAtom *)id; \
JSString *_str = ATOM_TO_STRING(_atom); \
const jschar *_cp = _str->chars; \
if (JS7_ISDEC(*_cp)) { \
jsint _index = JS7_UNDEC(*_cp); \
_cp++; \
if (_index != 0) { \
while (JS7_ISDEC(*_cp)) { \
_index = 10 * _index + JS7_UNDEC(*_cp); \
if (_index < 0) \
break; \
_cp++; \
} \
} \
if (*_cp == 0 && INT_FITS_IN_JSVAL(_index)) \
if (JS7_ISDEC(*_cp) && \
_str->length <= sizeof(JSVAL_INT_MAX_STRING)-1) \
{ \
jsuint _index = JS7_UNDEC(*_cp++); \
jsuint _oldIndex = 0; \
jsuint _c; \
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)))) \
id = INT_TO_JSVAL(_index); \
} else { \
CHECK_FOR_EMPTY_INDEX(id); \
@ -1266,6 +1273,7 @@ js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot)
} \
PR_END_MACRO
JSBool
js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
@ -1275,7 +1283,8 @@ js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
JSScope *scope;
JSScopeProperty *sprop;
/* Handle old bug that treated empty string as zero index. */
/* Handle old bug that treated empty string as zero index.
* Also convert string indices to numbers if applicable. */
CHECK_FOR_FUNNY_INDEX(id);
/* Lock if object locking is required by this implementation. */
@ -1345,7 +1354,8 @@ js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
JSObject *obj2, *proto;
JSScopeProperty *sprop;
/* Handle old bug that treated empty string as zero index. */
/* Handle old bug that treated empty string as zero index.
* Also convert string indices to numbers if applicable. */
CHECK_FOR_FUNNY_INDEX(id);
/* Search scopes starting with obj and following the prototype link. */
@ -1529,6 +1539,9 @@ js_FindVariableScope(JSContext *cx, JSFunction **funp)
;
if (fp)
obj = js_GetCallObject(cx, fp, parent, withobj);
} else if (clasp == &js_CallClass) {
fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
fun = fp->fun;
}
#endif
@ -1546,7 +1559,8 @@ js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
if (!js_LookupProperty(cx, obj, id, &obj2, (JSProperty **)&sprop))
return JS_FALSE;
if (!sprop) {
/* Handle old bug that treated empty string as zero index. */
/* Handle old bug that treated empty string as zero index.
* Also convert string indices to numbers if applicable. */
CHECK_FOR_FUNNY_INDEX(id);
#if JS_BUG_NULL_INDEX_PROPS
@ -1611,7 +1625,8 @@ js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
return JS_FALSE;
}
/* Handle old bug that treated empty string as zero index. */
/* Handle old bug that treated empty string as zero index.
* Also convert string indices to numbers if applicable. */
CHECK_FOR_FUNNY_INDEX(id);
hash = js_HashValue(id);
@ -1743,7 +1758,7 @@ js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
pval = LOCKED_OBJ_GET_SLOT(obj, slot);
/* Evil overloaded operator assign() hack. */
if (JSVAL_IS_OBJECT(pval)) {
if ((!JSVERSION_IS_ECMA(cx->version)) && (JSVAL_IS_OBJECT(pval))) {
assignobj = JSVAL_TO_OBJECT(pval);
if (assignobj) {
older = JS_SetErrorReporter(cx, NULL);
@ -1883,7 +1898,8 @@ js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
*rval = JSVERSION_IS_ECMA(cx->version) ? JSVAL_TRUE : JSVAL_VOID;
/* Handle old bug that treated empty string as zero index. */
/* Handle old bug that treated empty string as zero index.
* Also convert string indices to numbers if applicable. */
CHECK_FOR_FUNNY_INDEX(id);
if (!js_LookupProperty(cx, obj, id, &proto, &prop))
@ -2061,6 +2077,7 @@ JSBool
js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
jsval *statep, jsid *idp)
{
JSObject *proto_obj;
JSClass *clasp;
JSEnumerateOp enumerate;
JSScopeProperty *sprop;
@ -2073,7 +2090,7 @@ js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
enumerate = clasp->enumerate;
if (clasp->flags & JSCLASS_NEW_ENUMERATE)
return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp);
switch (enum_op) {
case JSENUMERATE_INIT:
@ -2088,24 +2105,40 @@ js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
*/
JS_LOCK_OBJ(cx, obj);
scope = (JSScope *) obj->map;
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) {
PR_ASSERT(i < length);
ida->vector[i++] = sym_id(sprop->symbols);
/*
* 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 == (JSScope *)proto_obj->map)) {
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) {
PR_ASSERT(i < length);
ida->vector[i++] = sym_id(sprop->symbols);
}
}
}
JS_UNLOCK_OBJ(cx, obj);
state = JS_malloc(cx, sizeof(JSNativeIteratorState));
if (!state) {
JS_DestroyIdArray(cx, ida);
@ -2117,7 +2150,7 @@ js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
if (idp)
*idp = INT_TO_JSVAL(length);
return JS_TRUE;
case JSENUMERATE_NEXT:
state = JSVAL_TO_PRIVATE(*statep);
ida = state->ida;
@ -2443,3 +2476,59 @@ out:
}
#endif /* JS_HAS_XDR */
#ifdef DEBUG
/* Routines to print out values during debugging. */
void printChar(jschar *cp) {
fprintf(stderr, "jschar* (0x%x) \"", cp);
while (*cp)
fputc(*cp++, stderr);
fputc('"', stderr);
fputc('\n', stderr);
}
void printString(JSString *str) {
jsuint i;
fprintf(stderr, "string (0x%x) \"", str);
for (i=0; i < str->length; i++)
fputc(str->chars[i], stderr);
fputc('"', stderr);
fputc('\n', stderr);
}
void printVal(jsval val) {
fprintf(stderr, "val %d (0x%x) = ", val, 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)) {
/* XXX can do more here */
fprintf(stderr, "object\n");
} 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 {
PR_ASSERT(JSVAL_IS_BOOLEAN(val));
fprintf(stderr, "(boolean) %s\n",
JSVAL_TO_BOOLEAN(val) ? "true" : "false");
}
fflush(stderr);
}
void printId(jsid id) {
fprintf(stderr, "id %d (0x%x) is ", id, id);
printVal(js_IdToValue(id));
}
void printAtom(JSAtom *atom) {
printString(ATOM_TO_STRING(atom));
}
#endif

View File

@ -417,7 +417,7 @@ js_NewPrinter(JSContext *cx, const char *name, uintN indent)
map = obj->map;
if (map->ops == &js_ObjectOps) {
if (OBJ_GET_CLASS(cx, obj) == &js_CallClass) {
obj = fp->fun->object;
obj = fp->fun ? fp->fun->object : NULL;
if (obj)
map = obj->map;
}
@ -819,37 +819,35 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
js_printf(jp, "\t}\n");
break;
case SRC_TRY:
js_printf(jp, "\ttry {\n");
case SRC_TRYFIN:
if (js_GetSrcNoteOffset(sn, 0) == 0) {
js_printf(jp, "\ttry {\n");
} else {
jp->indent -= 4;
js_printf(jp, "\t} finally {\n");
}
jp->indent += 4;
break;
case SRC_CATCH:
jp->indent -= 4;
sn = js_GetSrcNote(jp->script, pc);
pc += oplen;
js_printf(jp, "\t} catch ("); /* balance) */
sn = js_GetSrcNote(jp->script, pc);
len = js_CodeSpec[*pc].length;
switch (*pc) {
case JSOP_SETVAR:
lval = ATOM_BYTES(GetSlotAtom(jp->scope, js_GetLocalVariable,
GET_VARNO(pc)));
goto do_catchvar;
case JSOP_SETARG:
lval = ATOM_BYTES(GetSlotAtom(jp->scope, js_GetArgument,
GET_ARGNO(pc)));
goto do_catchvar;
case JSOP_SETNAME:
lval = ATOM_BYTES(GET_ATOM(cx, jp->script, pc));
do_catchvar:
js_printf(jp, "%s%s",
(sn && SN_TYPE(sn) == SRC_VAR ? "var " : ""), lval);
break;
default:
PR_ASSERT(0);
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; /* initprop, 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: {
@ -885,6 +883,17 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
todo = Sprint(&ss->sprinter, "");
break;
case JSOP_JSR:
case JSOP_RETSUB:
todo = -2;
break;
case JSOP_EXCEPTION:
sn = js_GetSrcNote(jp->script, pc);
if (sn && SN_TYPE(sn) == SRC_HIDDEN)
todo = -2;
break;
case JSOP_POP:
case JSOP_POPV:
sn = js_GetSrcNote(jp->script, pc);
@ -938,10 +947,13 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
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;
todo = -2;
break;
case JSOP_LEAVEWITH:
@ -963,9 +975,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
break;
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);
todo = -2;
break;
case JSOP_GOTO:
@ -1695,7 +1710,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
todo = -2;
break;
#ifdef JS_HAS_INITIALIZERS
#if JS_HAS_INITIALIZERS
case JSOP_NEWINIT:
LOCAL_ASSERT(ss->top >= 2);
(void) PopOff(ss, op);
@ -1767,6 +1782,13 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
#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;

View File

@ -190,3 +190,13 @@ OPDEF(JSOP_THROW, 110,"throw", NULL, 1, 1, 0, 0, JOF_BYTE)
/* 'in' and 'instanceof' ops. */
OPDEF(JSOP_IN, 111,js_in_str, js_in_str, 1, 2, 1, 6, JOF_BYTE)
OPDEF(JSOP_INSTANCEOF,112,js_instanceof_str,js_instanceof_str,1,2,1,6,JOF_BYTE)
/* debugger op */
OPDEF(JSOP_DEBUGGER, 113,"debugger", NULL, 1, 0, 0, 0, JOF_BYTE)
/* jsr/return for finally handling */
OPDEF(JSOP_JSR, 114,"jsr", NULL, 3, 0, 1, 0, JOF_JUMP)
OPDEF(JSOP_RETSUB, 115,"retsub", NULL, 1, 1, 0, 0, JOF_BYTE)
/* more exception handling ops */
OPDEF(JSOP_EXCEPTION, 116,"exception", NULL, 1, 0, 1, 0, JOF_BYTE)

View File

@ -69,6 +69,9 @@
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
@ -91,7 +94,7 @@ static JSParser ShiftExpr;
static JSParser AddExpr;
static JSParser MulExpr;
static JSParser UnaryExpr;
static JSParseNode *MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool allowCallSyntax);
static JSMemberParser MemberExpr;
static JSParser PrimaryExpr;
/*
@ -160,7 +163,7 @@ WellTerminated(JSContext *cx, JSTokenStream *ts, JSTokenType lastExprType)
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) {
(cx->version < JSVERSION_1_2) && (cx->version >= JSVERSION_1_0)) {
return JS_TRUE;
}
#endif
@ -244,6 +247,8 @@ out:
return ok;
}
#ifdef CHECK_RETURN_EXPR
/*
* 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
@ -286,6 +291,8 @@ CheckFinalReturn(JSParseNode *pn)
static char badreturn_str[] = "function does not always return a value";
#endif /* CHECK_RETURN_EXPR */
static JSParseNode *
FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun,
JSTreeContext *tc)
@ -373,10 +380,10 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
JSAtom *funAtom, *argAtom;
JSObject *parent;
JSFunction *fun, *outerFun;
JSStmtInfo *topStmt;
JSBool ok, named;
JSObject *pobj;
JSScopeProperty *sprop;
JSTreeContext funtc;
jsval junk;
/* Make a TOK_FUNCTION node. */
@ -395,10 +402,6 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
if (!parent)
return NULL;
/* Clear tc->topStmt for semantic checking (restore at label out:). */
topStmt = tc->topStmt;
tc->topStmt = NULL;
#if JS_HAS_LEXICAL_CLOSURE
if (!funAtom || cx->fp->scopeChain != parent || InWithStatement(tc)) {
/* Don't name the function if enclosed by a with statement or equiv. */
@ -485,7 +488,8 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
ok = JS_FALSE; goto out);
pn->pn_pos.begin = ts->token.pos.begin;
pn2 = FunctionBody(cx, ts, fun, tc);
INIT_TREE_CONTEXT(&funtc);
pn2 = FunctionBody(cx, ts, fun, &funtc);
if (!pn2) {
ok = JS_FALSE;
goto out;
@ -497,7 +501,7 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
pn->pn_fun = fun;
pn->pn_body = pn2;
pn->pn_tryCount = tc->tryCount;
pn->pn_tryCount = funtc.tryCount;
#if JS_HAS_LEXICAL_CLOSURE
if (outerFun || cx->fp->scopeChain != parent || InWithStatement(tc))
@ -510,7 +514,6 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
ok = JS_TRUE;
out:
tc->topStmt = topStmt;
if (!ok) {
if (named)
(void) OBJ_DELETE_PROPERTY(cx, parent, (jsid)funAtom, &junk);
@ -986,59 +989,41 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
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 will be used by the
* RelExpr production; if it flag is set, then the 'in' keyword
* will not be recognized as an operator, leaving it available to
* be parsed as part of a for/in loop. A side effect of this
* restriction is that (unparenthesized) expressions involving an
* 'in' operator are illegal in the init clause of an ordinary for
* loop.
*/
tc->flags |= TCF_IN_FOR_INIT;
#endif /* JS_HAS_IN_OPERATOR */
if (tt == TOK_VAR) {
(void) js_GetToken(cx, ts);
pn1 = Variables(cx, ts, tc);
} else {
pn1 = Expr(cx, ts, tc);
}
#if JS_HAS_IN_OPERATOR
tc->flags &= ~TCF_IN_FOR_INIT;
#endif /* JS_HAS_IN_OPERATOR */
if (!pn1)
return NULL;
}
/*
* There are three kinds of for/in syntax (see ECMA 12.6.3):
* 1. for (i in o) ...
* 2. for (var i in o) ...
* 3. for (var i = e in o) ...
* The 'var i = e in o' in 3 will be parsed as a variable declaration
* with an 'in' expression as its initializer, leaving ts->pushback at
* the right parenthesis. This condition tests 1, then 3, then 2:
*/
if (pn1 &&
(pn1->pn_type == TOK_IN ||
(pn1->pn_type == TOK_VAR && ts->pushback.type == TOK_RP) ||
js_MatchToken(cx, ts, TOK_IN))) {
* We can be sure that if it's a for/in loop, there's still an 'in'
* keyword here, even if Javascript recognizes it as an operator,
* because we've excluded it from parsing by setting the
* TCF_IN_FOR_INIT flag on the JSTreeContext argument.
*/
if (pn1 && js_MatchToken(cx, ts, TOK_IN)) {
stmtInfo.type = STMT_FOR_IN_LOOP;
switch (pn1->pn_type) {
case TOK_IN:
pn2 = pn1;
pn1 = pn1->pn_left;
break;
case TOK_VAR:
if (pn1->pn_count != 1) {
js_ReportCompileError(cx, ts, "invalid for/in variables");
return NULL;
}
pn2 = pn1->pn_head->pn_expr;
if (pn2) {
/* pn1 is 'var i = e' -- must pop e before loop. */
pn1->pn_op = JSOP_POP;
if (pn2->pn_type == TOK_IN) {
/* Found 'var i = e in o' -- make 'in' be the root. */
PR_ASSERT(ts->pushback.type == TOK_RP);
pn1->pn_head->pn_expr = pn2->pn_left;
pn2->pn_left = pn1;
break;
}
}
/* FALL THROUGH */
default:
pn2 = NULL;
break;
}
/* Check that the left side of the 'in' is valid. */
if (pn1->pn_type != TOK_VAR &&
pn1->pn_type != TOK_NAME &&
@ -1049,11 +1034,9 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
}
/* Parse the object expression as the right operand of 'in'. */
if (!pn2) {
pn2 = NewBinary(cx, TOK_IN, JSOP_NOP, pn1, Expr(cx, ts, tc));
if (!pn2)
return NULL;
}
pn2 = NewBinary(cx, TOK_IN, JSOP_NOP, pn1, Expr(cx, ts, tc));
if (!pn2)
return NULL;
pn->pn_left = pn2;
} else {
/* Parse the loop condition or null into pn2. */
@ -1102,114 +1085,108 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
return pn;
#if JS_HAS_EXCEPTIONS
case TOK_TRY:
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, &ts->token, PN_TERNARY);
pn1 = Statement(cx, ts, tc);
if (!pn1)
pn->pn_op = JSOP_NOP;
MUST_MATCH_TOKEN(TOK_LC, "missing { after before try block");
/* } balance */
pn->pn_kid1 = Statements(cx, ts, tc);
if (!pn->pn_kid1)
return NULL;
if (js_PeekToken(cx, ts) == TOK_CATCH) {
pn2 = NewParseNode(cx, &ts->token, PN_BINARY);
if (!pn2)
/* { balance */
MUST_MATCH_TOKEN(TOK_RC, "missing } after try block");
pn->pn_kid2 = NULL;
catchtail = pn;
while(js_PeekToken(cx, ts) == TOK_CATCH) {
/*
* legal catch forms are:
* catch (v)
* catch (v if <boolean_expression>)
*/
if (!catchtail->pn_kid1->pn_expr) {
js_ReportCompileError(cx, ts,
"catch clause after general catch");
return NULL;
(void)js_GetToken(cx, ts);
MUST_MATCH_TOKEN(TOK_LP, "missing ( after catch"); /* balance) */
tt = js_PeekToken(cx, ts);
if (tt == TOK_VAR) {
(void)js_GetToken(cx, ts);
pn4 = Variables(cx, ts, tc);
if (!pn4)
return NULL;
if (pn4->pn_count != 1) {
js_ReportCompileError(cx, ts,
"only one variable declaration permitted in catch declaration");
return NULL;
}
} else {
pn4 = Expr(cx, ts, tc);
if (!pn4)
return NULL;
/* restrict the expression to instanceof */
if (pn4->pn_op != JSOP_INSTANCEOF &&
pn4->pn_op != JSOP_NAME &&
pn4->pn_op != JSOP_GETARG &&
pn4->pn_op != JSOP_GETVAR) {
js_ReportCompileError(cx, ts,
"catch conditional must be instanceof or variable declaration");
PR_ASSERT(0);
return NULL;
}
}
/* rewrite the declaration/conditional expr as appropriate */
switch(pn4->pn_type) {
case TOK_NAME:
switch(pn4->pn_op) {
case JSOP_NAME:
pn4->pn_op = JSOP_SETNAME;
break;
case JSOP_GETARG:
pn4->pn_op = JSOP_SETARG;
break;
default:
PR_ASSERT(0);
}
break;
case TOK_VAR:
PR_ASSERT(pn4->pn_head->pn_type == TOK_NAME);
switch(pn4->pn_head->pn_op) {
case JSOP_GETVAR:
pn4->pn_head->pn_op = JSOP_SETVAR;
break;
case JSOP_GETARG:
pn4->pn_head->pn_op = JSOP_SETARG;
break;
case JSOP_NAME:
pn4->pn_head->pn_op = JSOP_SETNAME;
case JSOP_NOP:
break;
default:
PR_ASSERT(0);
}
break;
case TOK_INSTANCEOF:
PR_ASSERT(0);
default:
PR_ASSERT(0);
/* catch node */
pn2 = NewParseNode(cx, &ts->token, PN_TERNARY);
pn2->pn_op = JSOP_NOP;
/*
* We use a PN_NAME for the discriminant (catchguard) node
* with the actual discriminant code in the initializer spot
*/
pn3 = NewParseNode(cx, &ts->token, PN_NAME);
if (!pn2 || !pn3)
return NULL;
(void)js_GetToken(cx, ts); /* eat `catch' */
MUST_MATCH_TOKEN(TOK_LP, "missing ( after catch"); /* balance) */
MUST_MATCH_TOKEN(TOK_NAME, "missing identifier in catch");
pn3->pn_atom = ts->token.t_atom;
if (js_PeekToken(cx, ts) == TOK_COLON) {
(void)js_GetToken(cx, ts); /* eat `:' */
pn3->pn_expr = Expr(cx, ts, tc);
if (!pn3->pn_expr)
return NULL;
} else {
pn3->pn_expr = NULL;
}
pn2->pn_left = pn4;
/* (balance: */
MUST_MATCH_TOKEN(TOK_RP, "missing ) after catch declaration");
pn2->pn_right = Statement(cx, ts, tc);
if (!pn2->pn_right)
pn2->pn_kid1 = pn3;
/* ( balance: */
MUST_MATCH_TOKEN(TOK_RP, "missing ) after catch");
MUST_MATCH_TOKEN(TOK_LC, "missing { before catch block");
pn2->pn_kid3 = Statements(cx, ts, tc);
if (!pn2->pn_kid3)
return NULL;
} else {
pn2 = NULL;
MUST_MATCH_TOKEN(TOK_RC, "missing } after catch block");
catchtail = catchtail->pn_kid2 = pn2;
}
catchtail->pn_kid2 = NULL;
if (js_MatchToken(cx, ts, TOK_FINALLY)) {
pn3 = Statement(cx, ts, tc);
if (!pn3)
MUST_MATCH_TOKEN(TOK_LC, "missing { before finally block");
pn->pn_kid3 = Statements(cx, ts, tc);
if (!pn->pn_kid3)
return NULL;
MUST_MATCH_TOKEN(TOK_RC, "missing } after finally block");
} else {
pn3 = NULL;
pn->pn_kid3 = NULL;
}
if (!pn2 && !pn3) {
if (!pn->pn_kid2 && !pn->pn_kid3) {
js_ReportCompileError(cx, ts,
"missing catch or finally after try");
return NULL;
}
tc->tryCount++;
pn->pn_kid1 = pn1;
pn->pn_kid2 = pn2;
pn->pn_kid3 = pn3;
return pn;
}
case TOK_THROW:
pn = NewParseNode(cx, &ts->token, PN_UNARY);
if (!pn)
@ -1225,6 +1202,16 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
pn->pn_op = JSOP_THROW;
pn->pn_kid = pn2;
break;
/* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
case TOK_CATCH:
js_ReportCompileError(cx, ts, "catch without try");
return NULL;
case TOK_FINALLY:
js_ReportCompileError(cx, ts, "finally without try");
return NULL;
#endif /* JS_HAS_EXCEPTIONS */
case TOK_BREAK:
@ -1365,11 +1352,13 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
pn->pn_kid = NULL;
}
#ifdef CHECK_RETURN_EXPR
if ((tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) ==
(TCF_RETURN_EXPR | TCF_RETURN_VOID)) {
js_ReportCompileError(cx, ts, badreturn_str);
return NULL;
}
#endif
break;
case TOK_LC:
@ -1392,6 +1381,17 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
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, &ts->token, PN_NULLARY);
if (!pn)
return NULL;
pn->pn_type = TOK_DEBUGGER;
return pn;
#endif /* JS_HAS_DEBUGGER_KEYWORD */
case TOK_ERROR:
return NULL;
@ -1463,6 +1463,17 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
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.
*/
PR_ASSERT(ts->token.type == TOK_VAR);
pn = NewParseNode(cx, &ts->token, PN_LIST);
if (!pn)
@ -1475,8 +1486,13 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
return NULL;
clasp = OBJ_GET_CLASS(cx, obj);
if (fun && clasp == &js_FunctionClass) {
/* We are compiling code inside a function */
getter = js_GetLocalVariable;
setter = js_SetLocalVariable;
} else if (fun && clasp == &js_CallClass) {
/* We are compiling code from an eval inside a function */
getter = js_GetCallVariable;
setter = js_SetCallVariable;
} else {
getter = clasp->getProperty;
setter = clasp->setProperty;
@ -1517,7 +1533,25 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
PR_ASSERT(sprop->getter == js_GetLocalVariable);
PR_ASSERT(JSVAL_IS_INT(sprop->id) &&
JSVAL_TO_INT(sprop->id) < fun->nvars);
}
} else if (clasp == &js_CallClass) {
if (sprop->getter == js_GetCallVariable) {
/*
* Referencing a variable introduced by a var
* statement in the enclosing function. Check
* that the slot number we have is in range.
*/
PR_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.
*/
getter = sprop->getter;
setter = sprop->setter;
}
}
} else {
/* Global var: (re-)set id a la js_DefineProperty. */
sprop->id = ATOM_KEY(atom);
@ -1528,11 +1562,22 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
sprop->attrs &= ~JSPROP_READONLY;
}
} else {
/*
* Property not found in current variable scope: we have not
* seen this variable before.
* Define a new variable by adding a property to the current
* scope, or by allocating more slots in the function's frame.
*/
sprop = NULL;
if (prop) {
OBJ_DROP_PROPERTY(cx, pobj, prop);
prop = NULL;
}
if (getter == js_GetCallVariable) {
/* Can't increase fun->nvars in an active frame! */
getter = clasp->getProperty;
setter = clasp->setProperty;
}
ok = OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, JSVAL_VOID,
getter, setter,
JSPROP_ENUMERATE | JSPROP_PERMANENT,
@ -1540,9 +1585,14 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
if (ok && prop) {
pobj = obj;
if (getter == js_GetLocalVariable) {
/*
* Allocate more room for variables in the
* function's frame. We can do this only
* before the function is called.
*/
sprop = (JSScopeProperty *)prop;
sprop->id = INT_TO_JSVAL(fun->nvars++);
}
}
}
}
@ -1560,21 +1610,28 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
}
}
if (ok && fun && clasp == &js_FunctionClass && !InWithStatement(tc)) {
PR_ASSERT(sprop);
if (ok && fun && (clasp == &js_FunctionClass ||
clasp == &js_CallClass) &&
!InWithStatement(tc))
{
/* Depending on the value of the getter, change the
* opcodes to the forms for arguments and variables.
*/
if (getter == js_GetArgument) {
PR_ASSERT(sprop && JSVAL_IS_INT(sprop->id));
pn2->pn_op = (pn2->pn_op == JSOP_NAME)
? JSOP_GETARG
: JSOP_SETARG;
pn2->pn_slot = JSVAL_TO_INT(sprop->id);
} else if (getter == js_GetLocalVariable) {
} else if (getter == js_GetLocalVariable ||
getter == js_GetCallVariable)
{
PR_ASSERT(sprop && JSVAL_IS_INT(sprop->id));
pn2->pn_op = (pn2->pn_op == JSOP_NAME)
? JSOP_GETVAR
: JSOP_SETVAR;
pn2->pn_slot = JSVAL_TO_INT(sprop->id);
}
}
}
if (prop)
@ -1611,16 +1668,20 @@ Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
return pn;
}
/* ZZZbe don't create functions till codegen? or at least don't bind fn name */
/* ZZZbe don't create functions till codegen? or at least don't bind
* fn name */
static JSBool
LookupArgOrVar(JSContext *cx, JSAtom *atom, JSTreeContext *tc,
JSOp *opp, jsint *slotp)
{
JSObject *obj, *pobj;
JSClass *clasp;
JSFunction *fun;
JSScopeProperty *sprop;
obj = cx->fp->scopeChain;
if (OBJ_GET_CLASS(cx, obj) != &js_FunctionClass)
obj = js_FindVariableScope(cx, &fun);
clasp = OBJ_GET_CLASS(cx, obj);
if (clasp != &js_FunctionClass && clasp != &js_CallClass)
return JS_TRUE;
if (InWithStatement(tc))
return JS_TRUE;
@ -1632,7 +1693,9 @@ LookupArgOrVar(JSContext *cx, JSAtom *atom, JSTreeContext *tc,
if (sprop->getter == js_GetArgument) {
*opp = JSOP_GETARG;
*slotp = JSVAL_TO_INT(sprop->id);
} else if (sprop->getter == js_GetLocalVariable) {
} else if (sprop->getter == js_GetLocalVariable ||
sprop->getter == js_GetCallVariable)
{
*opp = JSOP_GETVAR;
*slotp = JSVAL_TO_INT(sprop->id);
}
@ -1683,6 +1746,9 @@ 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)) {
@ -1690,7 +1756,20 @@ CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
pn = NewParseNode(cx, &ts->token, PN_TERNARY);
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;
#endif /* JS_HAS_IN_OPERATOR */
if (!pn2)
return NULL;
MUST_MATCH_TOKEN(TOK_COLON, "missing : in conditional expression");
@ -1781,21 +1860,40 @@ 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
|| js_MatchToken(cx, ts, TOK_IN)
#endif
/*
* Only recognize the 'in' token as an operator 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
#endif /* JS_HAS_INSTANCEOF */
)) {
tt = ts->token.type;
op = ts->token.t_op;
pn = NewBinary(cx, tt, op, pn, ShiftExpr(cx, ts, tc));
}
#if JS_HAS_IN_OPERATOR
/* Restore previous state of inForInit flag. */
tc->flags |= inForInitFlag;
#endif /* JS_HAS_IN_OPERATOR */
return pn;
}
@ -1998,7 +2096,8 @@ UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
}
static JSParseNode *
ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSParseNode *listNode)
ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
JSParseNode *listNode)
{
JSBool matched;
@ -2020,17 +2119,18 @@ ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSParseNode *l
}
static JSParseNode *
MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool allowCallSyntax)
MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
JSBool allowCallSyntax)
{
JSParseNode *pn, *pn2, *pn3;
JSTokenType tt;
/* Check for new expressions */
/* 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);
(void) js_GetToken(cx, ts);
pn = NewParseNode(cx, &ts->token, PN_LIST);
if (!pn)
@ -2050,7 +2150,6 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool allowCall
return NULL;
}
pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
} else {
pn = PrimaryExpr(cx, ts, tc);
if (!pn)
@ -2279,10 +2378,26 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
#endif /* JS_HAS_INITIALIZERS */
case TOK_LP:
{
#if JS_HAS_IN_OPERATOR
uintN oldflags;
#endif
pn = NewParseNode(cx, &ts->token, PN_UNARY);
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 /* JS_HAS_IN_OPERATOR */
pn2 = Expr(cx, ts, tc);
#if JS_HAS_IN_OPERATOR
tc->flags = oldflags;
#endif /* JS_HAS_IN_OPERATOR */
if (!pn2)
return NULL;
@ -2292,9 +2407,12 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
pn->pn_pos.end = ts->token.pos.end;
pn->pn_kid = pn2;
break;
}
case TOK_STRING:
#if JS_HAS_SHARP_VARS
notsharp = JS_TRUE;
#endif /* JS_HAS_SHARP_VARS */
/* FALL THROUGH */
case TOK_NAME:
case TOK_OBJECT:
@ -2317,7 +2435,9 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
if (!pn)
return NULL;
pn->pn_dval = ts->token.t_dval;
#if JS_HAS_SHARP_VARS
notsharp = JS_TRUE;
#endif /* JS_HAS_SHARP_VARS */
break;
case TOK_PRIMARY:
@ -2325,7 +2445,9 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
if (!pn)
return NULL;
pn->pn_op = ts->token.t_op;
#if JS_HAS_SHARP_VARS
notsharp = JS_TRUE;
#endif /* JS_HAS_SHARP_VARS */
break;
#if !JS_HAS_EXPORT_IMPORT
@ -2358,7 +2480,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
defsharp->pn_kid = pn;
return defsharp;
}
#endif
#endif /* JS_HAS_SHARP_VARS */
return pn;
}

View File

@ -31,7 +31,7 @@
# elif defined(__GNUC__)
# define PR_INLINE inline
# else
# define PR_INLINE
# define PR_INLINE
# endif
#endif /* PR_INLINE */
@ -163,7 +163,7 @@ typedef struct JSXDRState JSXDRState;
#ifdef XP_OS2
#define CRT_CALL _Optlink
#else
#define CRT_CALL
#define CRT_CALL
#endif
#endif
@ -177,7 +177,7 @@ typedef JSBool
* 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 store
* stored in *statep. (You can use PRIVATE_TO_JSVAL() to store
* a pointer in *statep). The number of properties that will be
* enumerated should be returned as an integer jsval in *idp, if idp
* is non-NULL.
@ -291,3 +291,4 @@ typedef void
JSErrorReport *report);
#endif /* jspubtd_h___ */

View File

@ -1997,6 +1997,22 @@ js_NewRegExp(JSContext *cx, JSString *str, uintN flags)
goto out;
}
#ifdef DEBUG_notme
{
/* print the compiled regexp program bytecode */
jsuint i;
for (i=0; i < state.progLength; i++) {
int b = (int) re->program[i];
fprintf(stderr, "%d", b);
if ((i > 0 && i % 8 == 0) || i == state.progLength-1)
fprintf(stderr, "\n");
else
fprintf(stderr, ", ");
}
fprintf(stderr, "\n");
}
#endif
/* Success: lock re->source string. */
(void) js_LockGCThing(cx, str);
out:

View File

@ -131,10 +131,14 @@ static struct keyword {
#endif
#ifdef RESERVE_ECMA_KEYWORDS
{"debugger", TOK_RESERVED, JSOP_NOP, JSVERSION_1_3},
{"enum", TOK_RESERVED, JSOP_NOP, JSVERSION_1_3},
#endif
#if JS_HAS_DEBUGGER_KEYWORD
{"debugger", TOK_DEBUGGER, JSOP_NOP}, /* XXX version? */
#elif defined(RESERVE_ECMA_KEYWORDS)
{"debugger", TOK_RESERVED, JSOP_NOP, JSVERSION_1_3},
#endif
{0}
};
@ -253,8 +257,17 @@ static void
SendSourceToJSDebugger(JSTokenStream *ts, jschar *str, size_t length)
{
if (!ts->jsdsrc) {
ts->jsdsrc = JSD_NewSourceText(ts->jsdc,
ts->filename ? ts->filename : "typein");
const char* filename = ts->filename ? ts->filename : "typein";
if (1 == ts->lineno) {
ts->jsdsrc = JSD_NewSourceText(ts->jsdc, filename);
} else {
ts->jsdsrc = JSD_FindSourceForURL(ts->jsdc, filename);
if (ts->jsdsrc && JSD_SOURCE_PARTIAL !=
JSD_GetSourceStatus(ts->jsdc, ts->jsdsrc)) {
ts->jsdsrc = NULL;
}
}
}
if (ts->jsdsrc) {
/* here we convert our Unicode into a C string to pass to JSD */
@ -320,7 +333,7 @@ GetChar(JSTokenStream *ts)
}
}
for (j = 0; i < len; i++, j++)
ubuf[i] = (jschar) cbuf[j];
ubuf[i] = (jschar) (unsigned char) cbuf[j];
ts->userbuf.limit = ubuf + len;
ts->userbuf.ptr = ubuf;
} else
@ -668,7 +681,7 @@ retry:
if (JS7_ISDEC(c) || (c == '.' && JS7_ISDEC(PeekChar(ts)))) {
jsint radix;
jschar *endptr;
const jschar *endptr;
jsdouble dval;
radix = 10;
@ -683,7 +696,15 @@ retry:
RETURN(TOK_ERROR);
c = GetChar(ts);
radix = 16;
} else if (JS7_ISDEC(c)) {
} else if (JS7_ISDEC(c) && c < '8') {
/*
* XXX Warning needed. Checking against c < '8' above is
* non-ECMA, but is required to support legacy code; it's
* likely that "08" and "09" are in use in code having to do
* with dates. So we need to support it, which makes our
* behavior a superset of ECMA in this area. We should be
* raising a warning if '8' or '9' is encountered.
*/
radix = 8;
}
}
@ -729,16 +750,13 @@ retry:
FINISH_TOKENBUF(&ts->tokenbuf);
if (radix == 10) {
/* Let js_strtod() do the hard work and validity checks. */
if (!js_strtod(ts->tokenbuf.base, &endptr, &dval)) {
js_ReportCompileError(cx, ts,
"floating point literal out of range");
if (!js_strtod(cx, ts->tokenbuf.base, &endptr, &dval)) {
js_ReportCompileError(cx, ts, "out of memory");
RETURN(TOK_ERROR);
}
} else {
/* Let js_strtol() do the hard work, then check for overflow */
if (!js_strtol(ts->tokenbuf.base, &endptr, radix, &dval)) {
js_ReportCompileError(cx, ts, "integer literal too large");
if (!js_strtointeger(cx, ts->tokenbuf.base, &endptr, radix, &dval)) {
js_ReportCompileError(cx, ts, "out of memory");
RETURN(TOK_ERROR);
}
}

View File

@ -91,6 +91,7 @@ typedef enum JSTokenType {
TOK_FINALLY = 57, /* finally keyword */
TOK_THROW = 58, /* throw keyword */
TOK_INSTANCEOF = 59, /* instanceof keyword */
TOK_DEBUGGER = 60, /* debugger keyword */
TOK_RESERVED, /* reserved keywords */
TOK_LIMIT /* domain size */
} JSTokenType;

View File

@ -27,29 +27,31 @@
PR_BEGIN_EXTERN_C
/*
* Exception handling information.
* All fields are code offsets, relative to the beginning of the script.
* 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 start, catchStart, and finallyStart
* all equal to 0, and length == script->length.
*/
struct JSTryNote {
ptrdiff_t start; /* beginning of try{} region */
ptrdiff_t end; /* end of try{} region */
ptrdiff_t catch; /* beginning of catch{} (backptr during CG) */
ptrdiff_t finally; /* beginning of finally handler */
ptrdiff_t start; /* start of try statement */
ptrdiff_t length; /* count of try statement bytecodes */
ptrdiff_t catchStart; /* start of catch block (0 if none) */
};
struct JSScript {
jsbytecode *code; /* bytecodes and their immediate operands */
uint32 length; /* length of code vector */
JSAtomMap atomMap; /* maps immediate index to literal struct */
const char *filename; /* source filename or null */
uintN lineno; /* base line number of script */
uintN depth; /* maximum stack depth in slots */
jssrcnote *notes; /* line number and other decompiling data */
JSTryNote *trynotes; /* exception table for this script */
JSPrincipals *principals; /* principals for this script */
void *javaData; /* XXX extra data used by jsjava.c */
JSObject *object; /* optional Script-class object wrapper */
jsbytecode *code; /* bytecodes and their immediate operands */
uint32 length; /* length of code vector */
JSAtomMap atomMap; /* maps immediate index to literal struct */
const char *filename; /* source filename or null */
uintN lineno; /* base line number of script */
uintN depth; /* maximum stack depth in slots */
jssrcnote *notes; /* line number and other decompiling data */
JSTryNote *trynotes; /* exception table for this script */
JSPrincipals *principals; /* principals for this script */
void *javaData; /* XXX extra data used by jsjava.c */
JSObject *object; /* optional Script-class object wrapper */
};
extern JSClass js_ScriptClass;

View File

@ -73,7 +73,7 @@
static const unsigned char netCharType[256] =
/* Bit 0 xalpha -- the alphas
** Bit 1 xpalpha -- as xalpha but
** Bit 1 xpalpha -- as xalpha but
** converts spaces to plus and plus to %20
** Bit 2 ... path -- as xalphas but doesn't escape '/'
*/
@ -110,7 +110,7 @@ str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
if (!js_ValueToNumber(cx, argv[1], &d))
return JS_FALSE;
if (!JSDOUBLE_IS_FINITE(d) ||
(mask = (jsint)d) != d ||
(mask = (jsint)d) != d ||
mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH))
{
JS_ReportError(cx, "invalid string escape mask %x", mask);
@ -313,10 +313,10 @@ str_resolve(JSContext *cx, JSObject *obj, jsval id)
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;
slot = JSVAL_TO_INT(id);
if ((size_t)slot >= str->length)
return JS_TRUE;
return str_resolve1(cx, obj, str, slot);
@ -2242,6 +2242,15 @@ js_strchr(const jschar *s, jschar c)
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)
{
@ -2262,7 +2271,7 @@ js_InflateString(JSContext *cx, const char *bytes, size_t length)
if (!chars)
return NULL;
for (i = 0; i < length; i++)
chars[i] = (jschar) (unsigned char) bytes[i];
chars[i] = (unsigned char) bytes[i];
chars[i] = 0;
return chars;
}

View File

@ -232,6 +232,12 @@ 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

View File

@ -1,4 +1,4 @@
/* -*- Mode: C; tab-width: 8; c-basic-offset: 4 -*-
/* -*- 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.0 (the "NPL"); you may not use this file except in
@ -417,7 +417,7 @@ JS_XDRDouble(JSXDRState *xdr, jsdouble **dp)
}
return JS_TRUE;
}
JS_PUBLIC_API(JSBool)
JS_XDRValue(JSXDRState *xdr, jsval *vp)
{
@ -455,13 +455,13 @@ JS_XDRValue(JSXDRState *xdr, jsval *vp)
break;
}
case JSVAL_BOOLEAN: {
uint32 bool;
uint32 b;
if (xdr->mode == JSXDR_ENCODE)
bool = (uint32)JSVAL_TO_BOOLEAN(*vp);
if (!JS_XDRUint32(xdr, &bool))
b = (uint32)JSVAL_TO_BOOLEAN(*vp);
if (!JS_XDRUint32(xdr, &b))
return JS_FALSE;
if (xdr->mode == JSXDR_DECODE)
*vp = BOOLEAN_TO_JSVAL((JSBool)bool);
*vp = BOOLEAN_TO_JSVAL((JSBool)b);
break;
}
case JSVAL_VOID:
@ -469,6 +469,16 @@ JS_XDRValue(JSXDRState *xdr, jsval *vp)
return JS_FALSE;
break;
default:
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_ReportError(xdr->cx, "unknown jsval type %#lx for XDR", type);
return JS_FALSE;
}

View File

@ -1,4 +1,4 @@
/* -*- Mode: C; tab-width: 8; c-basic-offset: 4 -*-
/* -*- 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.0 (the "NPL"); you may not use this file except in

Binary file not shown.