Propagating work and bug fixes from

development branch:

- 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.
This commit is contained in:
mccabe%netscape.com 1998-07-30 23:46:29 +00:00
parent f0d7459661
commit 7596bde975
24 changed files with 653 additions and 343 deletions

View File

@ -42,6 +42,10 @@
#include "jsscope.h"
#include "jsscript.h"
#ifdef PERLCONNECT
#include "jsperl.h"
#endif
#ifdef LIVECONNECT
#include "jsjava.h"
#endif
@ -51,6 +55,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
@ -64,7 +71,7 @@
#define isatty(f) 1
#include <SIOUX.h>
#include <Types.h>
#include <MacTypes.h>
static char* mac_argv[] = { "js", NULL };
@ -72,12 +79,12 @@ static void initConsole(StringPtr consoleName, const char* startupMessage, int *
{
SIOUXSettings.autocloseonquit = true;
SIOUXSettings.asktosaveonclose = false;
// SIOUXSettings.initializeTB = false;
// SIOUXSettings.showstatusline = true;
/* SIOUXSettings.initializeTB = false;
SIOUXSettings.showstatusline = true;*/
puts(startupMessage);
SIOUXSetTitle(consoleName);
// set up a buffer for stderr (otherwise it's a pig).
/* set up a buffer for stderr (otherwise it's a pig). */
setvbuf(stderr, malloc(BUFSIZ), _IOLBF, BUFSIZ);
*argc = 1;
@ -261,8 +268,6 @@ my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report);
static JSBool
Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSContext *cx2;
JSVersion version;
uintN i;
JSString *str;
const char *filename;
@ -270,39 +275,22 @@ Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
JSBool ok;
jsval result;
/*
* Create new context to execute in so that gc will still find
* roots for the script that called load().
*/
cx2 = JS_NewContext(cx->runtime, 8192);
if (!cx2)
return JS_FALSE;
JS_SetErrorReporter(cx2, my_ErrorReporter);
JS_SetGlobalObject(cx2, JS_GetGlobalObject(cx));
version = JS_GetVersion(cx);
if (version != JSVERSION_DEFAULT)
JS_SetVersion(cx2, version);
ok = JS_TRUE;
for (i = 0; i < argc; i++) {
str = JS_ValueToString(cx, argv[i]);
if (!str) {
ok = JS_FALSE;
break;
}
if (!str)
return JS_FALSE;
argv[i] = STRING_TO_JSVAL(str);
filename = JS_GetStringBytes(str);
errno = 0;
script = JS_CompileFile(cx, obj, filename);
if (!script)
continue;
ok = JS_ExecuteScript(cx2, obj, script, &result);
ok = JS_ExecuteScript(cx, obj, script, &result);
JS_DestroyScript(cx, script);
if (!ok)
break;
return JS_FALSE;
}
JS_DestroyContext(cx2);
return ok;
return JS_TRUE;
}
static JSBool
@ -543,6 +531,11 @@ SrcNotes(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');
@ -573,10 +566,10 @@ TryNotes(JSContext *cx, JSFunction *fun)
if (!tn)
return JS_TRUE;
printf("\nException table:\nstart\tend\tcatch\tfinally\n");
while (tn->catchStart || tn->finallyStart) {
printf(" %d\t%d\t%d\t%d\n",
tn->start, tn->length, tn->catchStart, tn->finallyStart);
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;
@ -1285,9 +1278,10 @@ main(int argc, char **argv)
#endif
version = JSVERSION_DEFAULT;
#ifndef LIVECONNECT
argc--;
argv++;
#endif
rt = JS_NewRuntime(8L * 1024L * 1024L);
if (!rt)
return 1;
@ -1314,6 +1308,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;
@ -1340,6 +1339,9 @@ 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 */
result = ProcessArgs(cx, glob, argv, argc);

View File

@ -2541,7 +2541,7 @@ JS_GetPendingException(JSContext *cx, jsval *vp)
#if JS_HAS_EXCEPTIONS
if (!cx->fp->throwing)
return JS_FALSE;
*vp = cx->fp->rval;
*vp = cx->fp->exception;
return JS_TRUE;
#else
return JS_FALSE;
@ -2554,7 +2554,7 @@ JS_SetPendingException(JSContext *cx, jsval v)
CHECK_REQUEST(cx);
#if JS_HAS_EXCEPTIONS
cx->fp->throwing = JS_TRUE;
cx->fp->rval = v;
cx->fp->exception = v;
#endif
}

View File

@ -80,6 +80,8 @@ struct JSRuntime {
void *newScriptHookData;
JSDestroyScriptHook destroyScriptHook;
void *destroyScriptHookData;
JSTrapHandler debuggerHandler;
void *debuggerHandlerData;
/* More debugging state, see jsdbgapi.c. */
PRCList trapList;
@ -147,7 +149,7 @@ struct JSContext {
/* Client opaque pointer */
void *data;
/* Java environment and JS errors to throw as exceptions. */
void *javaEnv;
void *savedErrors;
@ -157,6 +159,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

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

@ -231,10 +231,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;
@ -434,6 +442,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)
{
@ -1069,24 +1128,46 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
#if JS_HAS_EXCEPTIONS
case TOK_TRY: {
ptrdiff_t start, end, catchStart;
ptrdiff_t start, end, catchStart, finallyCatch, catchjmp = -1;
JSParseNode *iter = pn;
#if shaver_finally
/* if this try has a finally, push marker onto treeContext */
/* 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,68 +1175,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) {
catchStart = end;
if (!js_EmitTree(cx, cg, pn->pn_kid2))
return JS_FALSE;
} else {
catchStart = 0;
/*
* 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);
}
#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))
/*
* 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 (!js_EmitTree(cx, cg, pn->pn_kid3))
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;
}
#endif
/* come from post-try */
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);
/* 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;
finallyIndex = CG_OFFSET(cg);
if (!FixupFinallyJumps(cx, cg, start, finallyIndex))
return JS_FALSE;
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;
}
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, catchStart, 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:
@ -1790,6 +2001,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);
}
@ -1818,7 +2034,7 @@ JS_FRIEND_DATA(const char *) js_SrcNoteName[] = {
"cont2label",
"switch",
"funcdef",
"try",
"tryfin",
"catch",
"newline",
"setline",
@ -1846,8 +2062,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 */
@ -2001,7 +2217,7 @@ js_GetSrcNoteOffset(jssrcnote *sn, uintN which)
sn += 2;
}
if (*sn & SN_3BYTE_OFFSET_FLAG)
return (ptrdiff_t)((((uint32)(*sn & SN_3BYTE_OFFSET_MASK)) << 16) | (sn[1] << 8) | sn[0]);
return (ptrdiff_t)((((uint32)(*sn & SN_3BYTE_OFFSET_MASK)) << 16) | (sn[1] << 8) | sn[2]);
return (ptrdiff_t)*sn;
}
@ -2089,7 +2305,7 @@ js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg)
JS_FRIEND_API(JSTryNote *)
js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t start,
ptrdiff_t end, ptrdiff_t catchStart, ptrdiff_t finallyStart)
ptrdiff_t end, ptrdiff_t catchStart)
{
JSTryNote *tn;
@ -2098,7 +2314,6 @@ js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t start,
tn->start = start;
tn->length = end - start;
tn->catchStart = catchStart;
tn->finallyStart = finallyStart;
return tn;
}
@ -2124,7 +2339,6 @@ js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg, JSTryNote **tryp)
final[count].start = 0;
final[count].length = CG_OFFSET(cg);
final[count].catchStart = 0;
final[count].finallyStart = 0;
*tryp = final;
return JS_TRUE;
}

View File

@ -248,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 */
@ -257,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 */
@ -362,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 catchStart, ptrdiff_t finallyStart);
ptrdiff_t end, ptrdiff_t catchStart);
/*
* Finish generating exception information, and copy it to JS_malloc

View File

@ -607,7 +607,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;
@ -695,8 +695,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) {
@ -728,6 +739,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

@ -390,6 +390,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
@ -506,6 +507,7 @@ have_fun:
frame.overrides = 0;
frame.debugging = JS_FALSE;
frame.throwing = JS_FALSE;
frame.dormantNext = NULL;
/* Compute the 'this' parameter and store it in frame. */
if (thisp && OBJ_GET_CLASS(cx, thisp) != &js_CallClass) {
@ -652,7 +654,7 @@ have_fun:
/* Native or script returns JS_FALSE on error or uncaught exception */
if (!ok && frame.throwing) {
fp->throwing = JS_TRUE;
fp->rval = frame.rval;
fp->exception = frame.exception;
}
#endif
@ -774,9 +776,37 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script, JSFunction *fun,
frame.overrides = 0;
frame.debugging = debugging;
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;
}
@ -1034,19 +1064,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) {
@ -1869,8 +1901,7 @@ js_Interpret(JSContext *cx, jsval *result)
cx->newborn[GCX_OBJECT] = NULL;
#if JS_HAS_EXCEPTIONS
if (fp->throwing) {
/* do_throw expects to have the exception on the stack */
PR_ASSERT(vp == sp - 1 && *vp == fp->rval);
sp[-1] = fp->exception;
goto do_throw;
}
#endif
@ -2091,7 +2122,7 @@ js_Interpret(JSContext *cx, jsval *result)
#if JS_HAS_EXCEPTIONS
if (fp->throwing) {
/* do_throw expects to have the exception on the stack */
PR_ASSERT(sp[-1] == fp->rval);
sp[-1] = fp->exception;
goto do_throw;
}
#endif
@ -2549,18 +2580,31 @@ 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 catch block.
*/
PR_ASSERT(!fp->throwing);
do_throw:
fp->exception = POP();
tn = script->trynotes;
offset = PTRDIFF(pc, script->code, jsbytecode);
if (tn) {
while (PR_UPTRDIFF(offset, tn->start) >= tn->length)
while (PR_UPTRDIFF(offset, tn->start) >= (pruword)tn->length)
tn++;
if (tn->catchStart) {
pc = script->code + tn->catchStart;
@ -2571,7 +2615,6 @@ js_Interpret(JSContext *cx, jsval *result)
/* Not in a catch block, so propagate the exception. */
fp->throwing = JS_TRUE;
fp->rval = POP();
ok = JS_FALSE;
goto out;
#endif /* JS_HAS_EXCEPTIONS */
@ -2601,6 +2644,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;
@ -2631,7 +2697,6 @@ js_Interpret(JSContext *cx, jsval *result)
}
#endif
}
out:
/*
* Restore the previous frame's execution state.
@ -2640,4 +2705,3 @@ out:
cx->interpLevel--;
return ok;
}

View File

@ -49,6 +49,8 @@ struct JSStackFrame {
uint8 overrides; /* bit-set of overridden Call properties */
JSPackedBool debugging; /* true if for JS_EvaluateInStackFrame */
JSPackedBool throwing; /* is there a pending exception? */
jsval exception; /* most-recently-thrown exceptin */
JSStackFrame *dormantNext; /* next dormant frame chain */
};
/*

View File

@ -815,37 +815,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: {
@ -881,6 +879,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);
@ -934,10 +943,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:
@ -959,9 +971,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:
@ -1763,6 +1778,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;
@ -1889,7 +1911,7 @@ js_DecompileFunction(JSPrinter *jp, JSFunction *fun, JSBool newlines)
}
if (atom == NULL)
break;
js_printf(jp, (i > 0 ? ", %s" : "%s"), ATOM_BYTES(atom));
js_printf(jp, (i > 0 ? ", %s" : "%s"), ATOM_BYTES(atom));
}
}
js_puts(jp, ") {\n");

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

@ -1081,114 +1081,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);
}
/* 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) */
tt = js_PeekToken(cx, ts);
if (tt == TOK_VAR) {
(void)js_GetToken(cx, ts);
pn4 = Variables(cx, ts, tc);
if (!pn4)
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;
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;
}
pn3->pn_expr = NULL;
}
pn2->pn_kid1 = pn3;
/* 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);
}
pn2->pn_left = pn4;
/* ( balance: */
MUST_MATCH_TOKEN(TOK_RP, "missing ) after catch");
/* (balance: */
MUST_MATCH_TOKEN(TOK_RP, "missing ) after catch declaration");
pn2->pn_right = Statement(cx, ts, tc);
if (!pn2->pn_right)
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)
@ -1204,6 +1198,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:
@ -1373,6 +1377,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;

View File

@ -127,10 +127,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}
};
@ -249,8 +253,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 */
@ -283,11 +296,11 @@ GetChar(JSTokenStream *ts)
int32 c;
ptrdiff_t len, olen;
jschar *nl;
if (ts->ungetpos != 0) {
c = ts->ungetbuf[--ts->ungetpos];
} else {
if (ts->linebuf.ptr == ts->linebuf.limit) {
if (ts->linebuf.ptr == ts->linebuf.limit) {
len = PTRDIFF(ts->userbuf.limit, ts->userbuf.ptr, jschar);
if (len <= 0) {
#ifdef JSFILE
@ -518,6 +531,7 @@ js_ReportCompileError(JSContext *cx, JSTokenStream *ts, const char *format,
fprintf(stderr, "%s:\n%s\n",message,
js_DeflateString(cx, ts->linebuf.base,
ts->linebuf.limit - ts->linebuf.base));
#endif
}
if (lastc == '\n')

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

@ -304,60 +304,6 @@ XDRAtomMap(JSXDRState *xdr, JSAtomMap *map)
return JS_TRUE;
}
#ifdef JS_XDR_SCRIPT_VERIFIER
/*
* Make sure that the script is "safe".
* We currently only check for a balanced stack budget.
*/
static JSBool
VerifyScript(JSContext *cx, JSScript *scr) {
int depth = 0;
jsbytecode *pc, *endpc;
pc = script->code;
endpc = pc + script->length; /* XXX attack based on falsified length? */
while (pc < endpc) {
JSCodeSpec *cs = &js_CodeSpec[(JSOp)*pc];
intN nuses = cs->nuses;
if (nuses < 0)
nuses = 2 + GET_ARGC(pc);
depth -= nuses;
if (depth < 0) { /* underflow */
JS_ReportError(cx,
"script verification failed: stack underflow\n");
return JS_FALSE;
}
depth += cs->ndefs;
if (depth > maxdepth) {
maxdepth = depth;
if (depth > script->depth) { /* overflow */
JS_ReportError(cx,
"script verification failed: stack overflow\n");
return JS_FALSE;
}
}
if (cs->format & JOF_TYPEMASK == JOF_TABLESWITCH ||
cs->format & JOF_TYPEMASK == JOF_LOOKUPSWITCH) {
/*
* the immediate operand is the length of the switch/lookup table
* (also the default offset)
*/
pc += GET_JUMP_OFFSET(pc);
} else {
pc += cs->length;
}
}
if (maxdepth != script->depth) { /* perhaps not attack, but malformed */
JS_ReportError(cx,
"script stack budget mismatch (max %d, declared %d)\n",
maxdepth, script->depth);
return JS_FALSE;
}
return JS_TRUE;
}
#endif
JSBool
js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *magic)
{
@ -402,13 +348,6 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *magic)
if (xdr->mode == JSXDR_DECODE) {
script->lineno = (uintN)lineno;
script->depth = (uintN)depth;
#ifdef JS_XDR_SCRIPT_VERIFIER
if (!VerifyScript(xdr->cx, script)) {
js_DestroyScript(xdr->cx, script);
*scriptp = NULL;
return JS_FALSE;
}
#endif
}
return JS_TRUE;
}

View File

@ -38,8 +38,6 @@ struct JSTryNote {
ptrdiff_t start; /* start of try statement */
ptrdiff_t length; /* count of try statement bytecodes */
ptrdiff_t catchStart; /* start of catch block (0 if none) */
ptrdiff_t finallyStart; /* XXX unneeded except for end-of-vector mark:
if (!catchStart && !finallyStart) break */
};
struct JSScript {

View File

@ -1,20 +1,19 @@
/* -*- 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 "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are Copyright (C) 1998
* Netscape Communications Corporation. All Rights Reserved.
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/*

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
@ -455,30 +455,30 @@ 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:
if (!JS_XDRUint32(xdr, (uint32 *)vp))
return JS_FALSE;
break;
case 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;
}
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

View File

@ -44,7 +44,7 @@ static PRArenaStats *arena_stats_list;
#define PR_ARENA_DEFAULT_ALIGN sizeof(double)
PR_PUBLIC_API(void)
PR_InitArenaPool(PRArenaPool *pool, const char *name, size_t size, size_t align)
PR_InitArenaPool(PRArenaPool *pool, const char *name, PRUint32 size, PRUint32 align)
{
if (align == 0)
align = PR_ARENA_DEFAULT_ALIGN;
@ -63,13 +63,13 @@ PR_InitArenaPool(PRArenaPool *pool, const char *name, size_t size, size_t align)
}
PR_PUBLIC_API(void *)
PR_ArenaAllocate(PRArenaPool *pool, size_t nb)
PR_ArenaAllocate(PRArenaPool *pool, PRUint32 nb)
{
PRArena **ap, *a, *b;
#ifdef JS_THREADSAFE
PRArena *c;
#endif
size_t sz;
PRUint32 sz;
void *p;
PR_ASSERT((nb & pool->mask) == 0);
@ -118,7 +118,7 @@ PR_ArenaAllocate(PRArenaPool *pool, size_t nb)
}
PR_PUBLIC_API(void *)
PR_ArenaGrow(PRArenaPool *pool, void *p, size_t size, size_t incr)
PR_ArenaGrow(PRArenaPool *pool, void *p, PRUint32 size, PRUint32 incr)
{
void *newp;
@ -258,7 +258,7 @@ PR_ArenaFinish()
#ifdef PR_ARENAMETER
PR_PUBLIC_API(void)
PR_ArenaCountAllocation(PRArenaPool *pool, size_t nb)
PR_ArenaCountAllocation(PRArenaPool *pool, PRUint32 nb)
{
pool->stats.nallocs++;
pool->stats.nbytes += nb;
@ -268,13 +268,13 @@ PR_ArenaCountAllocation(PRArenaPool *pool, size_t nb)
}
PR_PUBLIC_API(void)
PR_ArenaCountInplaceGrowth(PRArenaPool *pool, size_t size, size_t incr)
PR_ArenaCountInplaceGrowth(PRArenaPool *pool, PRUint32 size, PRUint32 incr)
{
pool->stats.ninplace++;
}
PR_PUBLIC_API(void)
PR_ArenaCountGrowth(PRArenaPool *pool, size_t size, size_t incr)
PR_ArenaCountGrowth(PRArenaPool *pool, PRUint32 size, PRUint32 incr)
{
pool->stats.ngrows++;
pool->stats.nbytes += incr;

View File

@ -164,8 +164,8 @@ struct PRArenaPool {
* with a minimum size per arena of size bytes.
*/
extern PR_PUBLIC_API(void)
PR_InitArenaPool(PRArenaPool *pool, const char *name, size_t size,
size_t align);
PR_InitArenaPool(PRArenaPool *pool, const char *name, PRUint32 size,
PRUint32 align);
/*
* Free the arenas in pool. The user may continue to allocate from pool
@ -197,10 +197,10 @@ PR_ArenaFinish(void);
* Friend functions used by the PR_ARENA_*() macros.
*/
extern PR_PUBLIC_API(void *)
PR_ArenaAllocate(PRArenaPool *pool, size_t nb);
PR_ArenaAllocate(PRArenaPool *pool, PRUint32 nb);
extern PR_PUBLIC_API(void *)
PR_ArenaGrow(PRArenaPool *pool, void *p, size_t size, size_t incr);
PR_ArenaGrow(PRArenaPool *pool, void *p, PRUint32 size, PRUint32 incr);
extern PR_PUBLIC_API(void)
PR_ArenaRelease(PRArenaPool *pool, char *mark);
@ -210,13 +210,13 @@ PR_ArenaRelease(PRArenaPool *pool, char *mark);
#include <stdio.h>
extern PR_PUBLIC_API(void)
PR_ArenaCountAllocation(PRArenaPool *pool, size_t nb);
PR_ArenaCountAllocation(PRArenaPool *pool, PRUint32 nb);
extern PR_PUBLIC_API(void)
PR_ArenaCountInplaceGrowth(PRArenaPool *pool, size_t size, size_t incr);
PR_ArenaCountInplaceGrowth(PRArenaPool *pool, PRUint32 size, PRUint32 incr);
extern PR_PUBLIC_API(void)
PR_ArenaCountGrowth(PRArenaPool *pool, size_t size, size_t incr);
PR_ArenaCountGrowth(PRArenaPool *pool, PRUint32 size, PRUint32incr);
extern PR_PUBLIC_API(void)
PR_ArenaCountRelease(PRArenaPool *pool, char *mark);

View File

@ -126,7 +126,7 @@ typedef struct PRTime PRTime;
#endif /* _WIN16 */
#else /* Mac or Unix */
#define PR_IMPLEMENT(__type) __type
#define PR_PUBLIC_API(__x) __x
#define PR_IMPORT_API(__x) __x
#define PR_PUBLIC_DATA(__x) __x
@ -134,8 +134,6 @@ typedef struct PRTime PRTime;
#define PR_CALLBACK
#define PR_STATIC_CALLBACK(__x) static __x
#define PR_EXTERN(__type) extern __type
#define PR_IMPLEMENT(__type) __type
#endif /* Mac or Unix */
/************************************************************************/