Merge tm to m-c.

This commit is contained in:
Robert Sayre 2009-01-15 02:42:06 -05:00
commit 6d6645ee0b
26 changed files with 897 additions and 465 deletions

View File

@ -109,6 +109,12 @@ else
AUTOMATION_PPARGS += -DIS_TEST_BUILD=0
endif
ifeq ($(MOZ_DEBUG), 1)
AUTOMATION_PPARGS += -DIS_DEBUG_BUILD=1
else
AUTOMATION_PPARGS += -DIS_DEBUG_BUILD=0
endif
_LEAKTEST_DIR = $(DEPTH)/_leaktest
_LEAKTEST_FILES = \

View File

@ -116,6 +116,12 @@ else
AUTOMATION_PPARGS += -DIS_TEST_BUILD=0
endif
ifeq ($(MOZ_DEBUG), 1)
AUTOMATION_PPARGS += -DIS_DEBUG_BUILD=1
else
AUTOMATION_PPARGS += -DIS_DEBUG_BUILD=0
endif
automation.py: automation.py.in
$(PYTHON) $(topsrcdir)/config/Preprocessor.py \
$(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $^ > $@

View File

@ -84,6 +84,7 @@ UNIXISH = not IS_WIN32 and not IS_MAC
#expand DEFAULT_APP = "./" + __BROWSER_PATH__
#expand CERTS_DIR = __CERTS_DIR__
#expand IS_TEST_BUILD = __IS_TEST_BUILD__
#expand IS_DEBUG_BUILD = __IS_DEBUG_BUILD__
###########
# LOGGING #
@ -315,6 +316,13 @@ user_pref("camino.warn_when_closing", false); // Camino-only, harmless to others
"""
prefs.append(part)
# Increase the max script run time 10-fold for debug builds
if (IS_DEBUG_BUILD):
prefs.append("""\
user_pref("dom.max_script_run_time", 100);
user_pref("dom.max_chrome_script_run_time", 200);
""")
locations = readLocations()
# Grant God-power to all the privileged servers on which tests run.

View File

@ -69,7 +69,7 @@ STATIC_PASS_TESTCASES = \
include $(topsrcdir)/config/rules.mk
LOCAL_INCLUDES += -I$(srcdir)/..
LOCAL_INCLUDES += -I$(srcdir)/.. -I..
check:: \
$(STATIC_FAILURE_TESTCASES:.cpp=.s-fail) \

View File

@ -160,7 +160,7 @@ STATIC CHAR *editinput();
#include <curses.h>
#include <term.h>
#endif /* defined(USE_TERMCAP) */
/*
** TTY input/output functions.
*/
@ -301,7 +301,7 @@ TTYinfo()
TTYrows = SCREEN_ROWS;
}
}
STATIC void
reposition()
@ -515,7 +515,7 @@ toggle_meta_mode()
rl_meta_chars = ! rl_meta_chars;
return redisplay();
}
STATIC CHAR *
next_hist()
@ -966,6 +966,9 @@ editinput()
case CSstay:
break;
}
if (strlen(Line))
return Line;
free(Line);
return NULL;
}
@ -1052,7 +1055,7 @@ add_history(p)
#endif /* defined(UNIQUE_HISTORY) */
hist_add((CHAR *)p);
}
STATIC STATUS
beg_line()

View File

@ -35,10 +35,13 @@
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/* NB: Keep this list synced with jitstatHandler in trace-test.js. */
JITSTAT(recorderStarted)
JITSTAT(recorderAborted)
JITSTAT(traceCompleted)
JITSTAT(sideExitIntoInterpreter)
JITSTAT(timeoutIntoInterpreter)
JITSTAT(typeMapMismatchAtEntry)
JITSTAT(returnToDifferentLoopHeader)
JITSTAT(traceTriggered)

View File

@ -5253,49 +5253,34 @@ JS_PUBLIC_API(void)
JS_SetOperationCallback(JSContext *cx, JSOperationCallback callback,
uint32 operationLimit)
{
JS_ASSERT(callback);
JS_ASSERT(operationLimit <= JS_MAX_OPERATION_LIMIT);
JS_ASSERT(operationLimit > 0);
cx->operationCount = (int32) operationLimit;
cx->operationLimit = operationLimit;
cx->operationCallbackIsSet = 1;
cx->operationCallback = callback;
JS_SetOperationCallbackFunction(cx, callback);
JS_SetOperationLimit(cx, operationLimit);
}
JS_PUBLIC_API(void)
JS_ClearOperationCallback(JSContext *cx)
{
cx->operationCount = (int32) JS_MAX_OPERATION_LIMIT;
cx->operationLimit = JS_MAX_OPERATION_LIMIT;
cx->operationCallbackIsSet = 0;
cx->operationCallback = NULL;
}
JS_PUBLIC_API(JSOperationCallback)
JS_GetOperationCallback(JSContext *cx)
{
JS_ASSERT(cx->operationCallbackIsSet || !cx->operationCallback);
return cx->operationCallback;
}
JS_PUBLIC_API(uint32)
JS_GetOperationLimit(JSContext *cx)
{
JS_ASSERT(cx->operationCallbackIsSet);
return cx->operationLimit;
JS_SetOperationCallbackFunction(cx, NULL);
JS_SetOperationLimit(cx, JS_MAX_OPERATION_LIMIT);
}
JS_PUBLIC_API(void)
JS_SetOperationLimit(JSContext *cx, uint32 operationLimit)
{
/* Mixed operation and branch callbacks are not supported. */
JS_ASSERT(!cx->branchCallbackWasSet);
JS_ASSERT(operationLimit <= JS_MAX_OPERATION_LIMIT);
JS_ASSERT(operationLimit > 0);
JS_ASSERT(cx->operationCallbackIsSet);
cx->operationCount = (int32) operationLimit;
cx->operationLimit = operationLimit;
if (cx->operationCount > (int32) operationLimit)
cx->operationCount = (int32) operationLimit;
}
JS_PUBLIC_API(uint32)
JS_GetOperationLimit(JSContext *cx)
{
JS_ASSERT(!cx->branchCallbackWasSet);
return cx->operationLimit;
}
JS_PUBLIC_API(JSBranchCallback)
@ -5303,14 +5288,16 @@ JS_SetBranchCallback(JSContext *cx, JSBranchCallback cb)
{
JSBranchCallback oldcb;
if (cx->operationCallbackIsSet) {
if (!cx->branchCallbackWasSet) {
#ifdef DEBUG
fprintf(stderr,
if (cx->operationCallback) {
fprintf(stderr,
"JS API usage error: call to JS_SetOperationCallback is followed by\n"
"invocation of deprecated JS_SetBranchCallback\n");
JS_ASSERT(0);
JS_ASSERT(0);
}
#endif
cx->operationCallbackIsSet = 0;
cx->branchCallbackWasSet = 1;
oldcb = NULL;
} else {
oldcb = (JSBranchCallback) cx->operationCallback;
@ -5320,11 +5307,32 @@ JS_SetBranchCallback(JSContext *cx, JSBranchCallback cb)
cx->operationLimit = JSOW_SCRIPT_JUMP;
cx->operationCallback = (JSOperationCallback) cb;
} else {
JS_ClearOperationCallback(cx);
cx->operationCallback = NULL;
}
return oldcb;
}
JS_PUBLIC_API(void)
JS_SetOperationCallbackFunction(JSContext *cx, JSOperationCallback callback)
{
/* Mixed operation and branch callbacks are not supported. */
JS_ASSERT(!cx->branchCallbackWasSet);
cx->operationCallback = callback;
}
JS_PUBLIC_API(JSOperationCallback)
JS_GetOperationCallback(JSContext *cx)
{
JS_ASSERT(!cx->branchCallbackWasSet);
return cx->operationCallback;
}
JS_PUBLIC_API(void)
JS_TriggerOperationCallback(JSContext *cx)
{
cx->operationCount = 0;
}
JS_PUBLIC_API(JSBool)
JS_IsRunning(JSContext *cx)
{

View File

@ -2174,13 +2174,26 @@ JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc,
* The maximum value of the operation limit to pass to JS_SetOperationCallback
* and JS_SetOperationLimit.
*/
#define JS_MAX_OPERATION_LIMIT ((uint32) 0x7FFFFFFF)
#define JS_MAX_OPERATION_LIMIT ((uint32) 0x7FFFFFFF - (uint32) 1)
#define JS_OPERATION_WEIGHT_BASE 4096
extern JS_PUBLIC_API(void)
JS_SetOperationCallbackFunction(JSContext *cx, JSOperationCallback callback);
extern JS_PUBLIC_API(JSOperationCallback)
JS_GetOperationCallback(JSContext *cx);
/*
* Set the operation callback that the engine calls periodically after
* the internal operation count reaches the specified limit.
* Force a call to operation callback at some later moment. The function can be
* called from an arbitrary thread for any context.
*/
extern JS_PUBLIC_API(void)
JS_TriggerOperationCallback(JSContext *cx);
/*
* Set the limit for the internal operation counter. The engine calls the
* operation callback When the limit is reached.
*
* When operationLimit is JS_OPERATION_WEIGHT_BASE, the callback will be
* called at least after each backward jump in the interpreter. To minimize
@ -2191,14 +2204,7 @@ JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc,
* as a value for operationLimit.
*/
extern JS_PUBLIC_API(void)
JS_SetOperationCallback(JSContext *cx, JSOperationCallback callback,
uint32 operationLimit);
extern JS_PUBLIC_API(void)
JS_ClearOperationCallback(JSContext *cx);
extern JS_PUBLIC_API(JSOperationCallback)
JS_GetOperationCallback(JSContext *cx);
JS_SetOperationLimit(JSContext *cx, uint32 operationLimit);
/*
* Get the operation limit associated with the operation callback. This API
@ -2208,13 +2214,12 @@ JS_GetOperationCallback(JSContext *cx);
extern JS_PUBLIC_API(uint32)
JS_GetOperationLimit(JSContext *cx);
/*
* Change the operation limit associated with the operation callback. This API
* function may be called only when the result of JS_GetOperationCallback(cx)
* is not null.
*/
extern JS_PUBLIC_API(void)
JS_SetOperationLimit(JSContext *cx, uint32 operationLimit);
JS_SetOperationCallback(JSContext *cx, JSOperationCallback callback,
uint32 operationLimit);
extern JS_PUBLIC_API(void)
JS_ClearOperationCallback(JSContext *cx);
/*
* Note well: JS_SetBranchCallback is deprecated. It is similar to

View File

@ -398,13 +398,15 @@ js_AddProperty(JSContext* cx, JSObject* obj, JSScopeProperty* sprop)
return JS_FALSE;
}
JSBool FASTCALL
js_HasNamedProperty(JSContext* cx, JSObject* obj, JSString* idstr)
static JSBool
HasProperty(JSContext* cx, JSObject* obj, jsid id)
{
jsid id;
if (!js_ValueToStringId(cx, STRING_TO_JSVAL(idstr), &id))
// check whether we know how the resolve op will behave
JSClass* clasp = OBJ_GET_CLASS(cx, obj);
if (clasp->resolve != JS_ResolveStub && clasp != &js_StringClass)
return JSVAL_TO_BOOLEAN(JSVAL_VOID);
JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED);
JSObject* obj2;
JSProperty* prop;
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
@ -414,6 +416,16 @@ js_HasNamedProperty(JSContext* cx, JSObject* obj, JSString* idstr)
return prop != NULL;
}
JSBool FASTCALL
js_HasNamedProperty(JSContext* cx, JSObject* obj, JSString* idstr)
{
jsid id;
if (!js_ValueToStringId(cx, STRING_TO_JSVAL(idstr), &id))
return JSVAL_TO_BOOLEAN(JSVAL_VOID);
return HasProperty(cx, obj, id);
}
JSBool FASTCALL
js_HasNamedPropertyInt32(JSContext* cx, JSObject* obj, int32 index)
{
@ -421,13 +433,7 @@ js_HasNamedPropertyInt32(JSContext* cx, JSObject* obj, int32 index)
if (!js_Int32ToId(cx, index, &id))
return JSVAL_TO_BOOLEAN(JSVAL_VOID);
JSObject* obj2;
JSProperty* prop;
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
return JSVAL_TO_BOOLEAN(JSVAL_VOID);
if (prop)
OBJ_DROP_PROPERTY(cx, obj2, prop);
return prop != NULL;
return HasProperty(cx, obj, id);
}
jsval FASTCALL

View File

@ -78,6 +78,9 @@
static PRUintn threadTPIndex;
static JSBool tpIndexInited = JS_FALSE;
static void
InitOperationLimit(JSContext *cx);
JS_BEGIN_EXTERN_C
JSBool
js_InitThreadPrivateIndex(void (*ptr)(void *))
@ -251,7 +254,7 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize)
memset(cx, 0, sizeof *cx);
cx->runtime = rt;
JS_ClearOperationCallback(cx);
js_InitOperationLimit(cx);
cx->debugHooks = &rt->globalDebugHooks;
#if JS_STACK_GROWTH_DIRECTION > 0
cx->stackLimit = (jsuword)-1;
@ -1355,10 +1358,11 @@ js_ResetOperationCount(JSContext *cx)
JS_ASSERT(cx->operationLimit > 0);
cx->operationCount = (int32) cx->operationLimit;
if (cx->operationCallbackIsSet)
return cx->operationCallback(cx);
JSOperationCallback cb = cx->operationCallback;
if (cb) {
if (!cx->branchCallbackWasSet)
return cb(cx);
if (cx->operationCallback) {
/*
* Invoke the deprecated branch callback. It may be called only when
* the top-most frame is scripted or JSOPTION_NATIVE_BRANCH_CALLBACK
@ -1367,7 +1371,7 @@ js_ResetOperationCount(JSContext *cx)
fp = js_GetTopStackFrame(cx);
script = fp ? fp->script : NULL;
if (script || JS_HAS_OPTION(cx, JSOPTION_NATIVE_BRANCH_CALLBACK))
return ((JSBranchCallback) cx->operationCallback)(cx, script);
return ((JSBranchCallback) cb)(cx, script);
}
return JS_TRUE;
}

View File

@ -750,7 +750,7 @@ struct JSContext {
* Operation count. It is declared as the first field in the struct to
* ensure the fastest possible access.
*/
int32 operationCount;
volatile int32 operationCount;
/* JSRuntime contextList linkage. */
JSCList link;
@ -860,11 +860,10 @@ struct JSContext {
JSErrorReporter errorReporter;
/*
* Flag indicating that the operation callback is set. When the flag is 0
* but operationCallback is not null, operationCallback stores the branch
* Flag indicating that operationCallback stores the deprecated branch
* callback.
*/
uint32 operationCallbackIsSet : 1;
uint32 branchCallbackWasSet : 1;
uint32 operationLimit : 31;
JSOperationCallback operationCallback;
@ -1274,6 +1273,23 @@ extern JSErrorFormatString js_ErrorFormatString[JSErr_Limit];
extern JSBool
js_ResetOperationCount(JSContext *cx);
static JS_INLINE void
js_InitOperationLimit(JSContext *cx)
{
/*
* Set the limit to 1 + max to detect if JS_SetOperationLimit() was ever
* called.
*/
cx->operationCount = (int32) JS_MAX_OPERATION_LIMIT + 1;
cx->operationLimit = JS_MAX_OPERATION_LIMIT + 1;
}
static JS_INLINE JSBool
js_HasOperationLimit(JSContext *cx)
{
return cx->operationLimit <= JS_MAX_OPERATION_LIMIT;
}
/*
* Get the current cx->fp, first lazily instantiating stack frames if needed.
* (Do not access cx->fp directly except in JS_REQUIRES_STACK code.)

View File

@ -5994,7 +5994,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
* array comprehension, use JSOP_NEWARRAY.
*/
pn2 = pn->pn_head;
op = JSOP_NEWINIT; // FIXME: 260106 patch disabled for now
op = JSOP_NEWARRAY;
#if JS_HAS_SHARP_VARS
if (pn2 && pn2->pn_type == TOK_DEFSHARP)
@ -6639,7 +6639,7 @@ js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index,
/*
* Simultaneously test to see if the source note array must grow to
* accomodate either the first or second byte of additional storage
* accommodate either the first or second byte of additional storage
* required by this 3-byte offset.
*/
if (((CG_NOTE_COUNT(cg) + 1) & CG_NOTE_MASK(cg)) <= 1) {

View File

@ -2669,10 +2669,8 @@ js_Interpret(JSContext *cx)
*/
#define CHECK_BRANCH() \
JS_BEGIN_MACRO \
if ((cx->operationCount -= JSOW_SCRIPT_JUMP) <= 0) { \
if (!js_ResetOperationCount(cx)) \
goto error; \
} \
if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_SCRIPT_JUMP)) \
goto error; \
JS_END_MACRO
#define BRANCH(n) \

View File

@ -298,8 +298,8 @@ num_toSource(JSContext *cx, uintN argc, jsval *vp)
#endif
/* The buf must be big enough for MIN_INT to fit including '-' and '\0'. */
char *
js_IntToCString(jsint i, jsint base, char *buf, size_t bufSize)
static char *
IntToCString(jsint i, jsint base, char *buf, size_t bufSize)
{
char *cp;
jsuint u;
@ -363,7 +363,7 @@ num_toString(JSContext *cx, uintN argc, jsval *vp)
return JS_FALSE;
if (base < 2 || base > 36) {
char numBuf[12];
char *numStr = js_IntToCString(base, 10, numBuf, sizeof numBuf);
char *numStr = IntToCString(base, 10, numBuf, sizeof numBuf);
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_RADIX,
numStr);
return JS_FALSE;
@ -787,15 +787,21 @@ js_NewNumberInRootedValue(JSContext *cx, jsdouble d, jsval *vp)
return js_NewDoubleInRootedValue(cx, d, vp);
}
char *
js_NumberToCString(JSContext *cx, jsdouble d, jsint base, char *buf, size_t bufSize)
/*
* Convert a number to C string. The buf must be large enough to accommodate
* the result, including '-' and '\0', if base == 10 or d is an integer that
* fits in 32 bits. The caller must free the resulting pointer if it does not
* point into buf.
*/
static char *
NumberToCString(JSContext *cx, jsdouble d, jsint base, char *buf, size_t bufSize)
{
jsint i;
char *numStr;
JS_ASSERT(bufSize >= DTOSTR_STANDARD_BUFFER_SIZE);
if (JSDOUBLE_IS_INT(d, i)) {
numStr = js_IntToCString(i, base, buf, bufSize);
numStr = IntToCString(i, base, buf, bufSize);
} else {
if (base == 10)
numStr = JS_dtostr(buf, bufSize, DTOSTR_STANDARD, 0, d);
@ -812,15 +818,25 @@ js_NumberToCString(JSContext *cx, jsdouble d, jsint base, char *buf, size_t bufS
static JSString * JS_FASTCALL
NumberToStringWithBase(JSContext *cx, jsdouble d, jsint base)
{
char buf[DTOSTR_STANDARD_BUFFER_SIZE];
/*
* The longest possible result here that would need to fit in buf is
* (-0x80000000).toString(2), which has length 33. (This can produce
* longer results, but in those cases buf is not used; see comment at
* NumberToCString.)
*/
char buf[34];
char *numStr;
JSString *s;
if (base < 2 || base > 36)
return NULL;
numStr = js_NumberToCString(cx, d, base, buf, sizeof buf);
numStr = NumberToCString(cx, d, base, buf, sizeof buf);
if (!numStr)
return NULL;
return JS_NewStringCopyZ(cx, numStr);
s = JS_NewStringCopyZ(cx, numStr);
if (!(numStr >= buf && numStr < buf + sizeof buf))
free(numStr);
return s;
}
JSString * JS_FASTCALL

View File

@ -182,20 +182,6 @@ js_NewNumberInRootedValue(JSContext *cx, jsdouble d, jsval *vp);
extern JSString * JS_FASTCALL
js_NumberToString(JSContext *cx, jsdouble d);
/*
* Convert int to C string. The buf must be big enough for MIN_INT to fit
* including '-' and '\0'.
*/
char *
js_IntToCString(jsint i, jsint base, char *buf, size_t bufSize);
/*
* Convert a number to C string. The buf must be at least
* DTOSTR_STANDARD_BUFFER_SIZE.
*/
char *
js_NumberToCString(JSContext *cx, jsdouble d, jsint base, char *buf, size_t bufSize);
/*
* Convert a value to a number. On exit JSVAL_IS_NULL(*vp) iff there was an
* error. If on exit JSVAL_IS_NUMBER(*vp), then *vp holds the jsval that

View File

@ -1,4 +1,4 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set sw=4 ts=8 et tw=99:
*
* ***** BEGIN LICENSE BLOCK *****
@ -3468,20 +3468,16 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
#if JS_HAS_LVALUE_RETURN
case JSOP_SETCALL:
#endif
/* Turn off most parens (all if there's only one argument). */
argc = GET_ARGC(pc);
op = (argc == 1) ? JSOP_NOP : JSOP_SETNAME;
argv = (char **)
JS_malloc(cx, (size_t)(argc + 1) * sizeof *argv);
if (!argv)
return NULL;
op = JSOP_SETNAME;
ok = JS_TRUE;
for (i = argc; i > 0; i--) {
for (i = argc; i > 0; i--)
argv[i] = JS_strdup(cx, POP_STR());
if (!argv[i])
ok = JS_FALSE;
}
/* Skip the JSOP_PUSHOBJ-created empty string. */
LOCAL_ASSERT(ss->top >= 2);
@ -3492,7 +3488,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
* Same for new (x(y).z) -- contrast with new x(y).z.
* See PROPAGATE_CALLNESS.
*/
op = (JSOp) ss->opcodes[ss->top-1];
op = (JSOp) ss->opcodes[ss->top - 1];
lval = PopStr(ss,
(saveop == JSOP_NEW &&
(op == JSOP_CALL ||
@ -3504,7 +3500,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
op = saveop;
argv[0] = JS_strdup(cx, lval);
if (!argv[i])
if (!argv[0])
ok = JS_FALSE;
lval = "(", rval = ")";
@ -3531,10 +3527,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
if (Sprint(&ss->sprinter, rval) < 0)
ok = JS_FALSE;
for (i = 0; i <= argc; i++) {
if (argv[i])
JS_free(cx, argv[i]);
}
for (i = 0; i <= argc; i++)
JS_free(cx, argv[i]);
JS_free(cx, argv);
if (!ok)
return NULL;
@ -3931,7 +3925,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
/*
* All allocation when decompiling is LIFO, using malloc
* or, more commonly, arena-alloocating from cx->tempPool.
* or, more commonly, arena-allocating from cx->tempPool.
* After InitSprintStack succeeds, we must release to mark
* before returning.
*/
@ -4287,53 +4281,48 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
break;
case JSOP_NEWARRAY:
{
ptrdiff_t off;
char *base, *from, *to;
/*
* All operands are stacked and ready for in-place formatting.
* We know that PAREN_SLOP is 3 here, and take advantage of it
* to avoid strdup'ing.
*/
argc = GET_UINT24(pc);
LOCAL_ASSERT(ss->top >= (uintN) argc);
sn = js_GetSrcNote(jp->script, pc);
if (argc == 0) {
todo = Sprint(&ss->sprinter, "[%s]",
(sn && SN_TYPE(sn) == SRC_CONTINUE)
? ", "
: "");
} else {
ss->top -= argc;
off = GetOff(ss, ss->top);
LOCAL_ASSERT(off >= PAREN_SLOP);
base = OFF2STR(&ss->sprinter, off);
to = base + 1;
i = 0;
for (;;) {
/* Move to the next string that had been stacked. */
from = OFF2STR(&ss->sprinter, off);
todo = strlen(from);
memmove(to, from, todo);
to += todo;
if (++i == argc &&
!(sn && SN_TYPE(sn) == SRC_CONTINUE)) {
break;
}
*to++ = ',';
*to++ = ' ';
off = GetOff(ss, ss->top + i);
}
LOCAL_ASSERT(to - base < ss->sprinter.offset - PAREN_SLOP);
*base = '[';
*to++ = ']';
*to = '\0';
ss->sprinter.offset = STR2OFF(&ss->sprinter, to);
todo = STR2OFF(&ss->sprinter, base);
todo = SprintCString(&ss->sprinter, "[]");
break;
}
argv = (char **) JS_malloc(cx, size_t(argc) * sizeof *argv);
if (!argv)
return NULL;
op = JSOP_SETNAME;
ok = JS_TRUE;
i = argc;
while (i > 0)
argv[--i] = JS_strdup(cx, POP_STR());
todo = SprintCString(&ss->sprinter, "[");
if (todo < 0)
break;
for (i = 0; i < argc; i++) {
if (!argv[i] ||
Sprint(&ss->sprinter, ss_format,
argv[i], (i < argc - 1) ? ", " : "") < 0) {
ok = JS_FALSE;
break;
}
}
for (i = 0; i < argc; i++)
JS_free(cx, argv[i]);
JS_free(cx, argv);
if (!ok)
return NULL;
sn = js_GetSrcNote(jp->script, pc);
if (sn && SN_TYPE(sn) == SRC_CONTINUE && SprintCString(&ss->sprinter, ", ") < 0)
return NULL;
if (SprintCString(&ss->sprinter, "]") < 0)
return NULL;
break;
}
case JSOP_NEWINIT:
{

View File

@ -1,5 +1,5 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=0 ft=C:
* vim: set ts=8 sw=4 et tw=0 ft=c:
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
@ -110,7 +110,7 @@
*/
OPDEF(JSOP_NOP, 0, "nop", NULL, 1, 0, 0, 0, JOF_BYTE)
/* Longstanding JavaScript bytecodes. */
/* Long-standing JavaScript bytecodes. */
OPDEF(JSOP_PUSH, 1, "push", NULL, 1, 0, 1, 0, JOF_BYTE)
OPDEF(JSOP_POPV, 2, "popv", NULL, 1, 1, 0, 2, JOF_BYTE)
OPDEF(JSOP_ENTERWITH, 3, "enterwith", NULL, 1, 1, 1, 0, JOF_BYTE|JOF_PARENHEAD)
@ -127,7 +127,7 @@ OPDEF(JSOP_ARGUMENTS, 9, js_arguments_str, js_arguments_str, 1, 0, 1, 18, JOF_BY
OPDEF(JSOP_FORARG, 10, "forarg", NULL, 3, 2, 2, 19, JOF_QARG|JOF_NAME|JOF_FOR)
OPDEF(JSOP_FORLOCAL, 11, "forlocal", NULL, 3, 2, 2, 19, JOF_LOCAL|JOF_NAME|JOF_FOR)
/* More longstanding bytecodes. */
/* More long-standing bytecodes. */
OPDEF(JSOP_DUP, 12, "dup", NULL, 1, 1, 2, 0, JOF_BYTE)
OPDEF(JSOP_DUP2, 13, "dup2", NULL, 1, 2, 4, 0, JOF_BYTE)
OPDEF(JSOP_SETCONST, 14, "setconst", NULL, 3, 1, 1, 3, JOF_ATOM|JOF_NAME|JOF_SET)
@ -559,8 +559,17 @@ OPDEF(JSOP_INT32, 228, "int32", NULL, 5, 0, 1, 16, JOF_INT32)
OPDEF(JSOP_LENGTH, 229, "length", NULL, 1, 1, 1, 18, JOF_BYTE|JOF_PROP)
/*
* JSOP_NEWARRAY optimizes array literal evaluation using the interpreter stack.
* JSOP_HOLE pushes a JSVAL_HOLE (used with JSOP_NEWINIT and JSOP_NEWARRAY).
* Construct a new dense array whose contents are the values provided on the
* stack, consuming those values and replacing them with the newly-constructed
* array. The topmost value is the last value in the new array, and the
* bottommost value is the first value in the array; the array length is a
* 24-bit immediate operand to the instruction.
*/
OPDEF(JSOP_NEWARRAY, 230, "newarray", NULL, 4, -1, 1, 19, JOF_UINT24)
/*
* Push a JSVAL_HOLE value onto the stack, representing an omitted property in
* an array literal (e.g. property 0 in the array [, 1]). This opcode is used
* with the JSOP_NEWARRAY and JSOP_NEWINIT opcodes.
*/
OPDEF(JSOP_HOLE, 231, "hole", NULL, 1, 0, 1, 0, JOF_BYTE)

View File

@ -2197,6 +2197,9 @@ str_concat(JSContext *cx, uintN argc, jsval *vp)
NORMALIZE_THIS(cx, vp, str);
/* Set vp (aka rval) early to handle the argc == 0 case. */
*vp = STRING_TO_JSVAL(str);
for (i = 0, argv = vp + 2; i < argc; i++) {
str2 = js_ValueToString(cx, argv[i]);
if (!str2)
@ -2206,9 +2209,9 @@ str_concat(JSContext *cx, uintN argc, jsval *vp)
str = js_ConcatStrings(cx, str, str2);
if (!str)
return JS_FALSE;
*vp = STRING_TO_JSVAL(str);
}
*vp = STRING_TO_JSVAL(str);
return JS_TRUE;
}

View File

@ -303,9 +303,9 @@ Tracker::has(const void *v) const
}
#if defined NANOJIT_64BIT
#define PAGEMASK 0x7ff
#define PAGEMASK 0x7ff
#else
#define PAGEMASK 0xfff
#define PAGEMASK 0xfff
#endif
LIns*
@ -372,7 +372,6 @@ hash_accum(uintptr_t& h, uintptr_t i)
h = ((h << 5) + h + (ORACLE_MASK & i)) & ORACLE_MASK;
}
JS_REQUIRES_STACK static inline int
stackSlotHash(JSContext* cx, unsigned slot)
{
@ -1065,9 +1064,14 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* _anchor, Fragment* _frag
if (fragment == fragment->root) {
LIns* counter = lir->insLoadi(cx_ins,
offsetof(JSContext, operationCount));
LIns* updated = lir->ins2i(LIR_sub, counter, JSOW_SCRIPT_JUMP);
lir->insStorei(updated, cx_ins, offsetof(JSContext, operationCount));
guard(false, lir->ins2i(LIR_le, updated, 0), snapshot(TIMEOUT_EXIT));
if (js_HasOperationLimit(cx)) {
/* Add code to decrease the operationCount if the embedding relies
on its auto-updating. */
counter = lir->ins2i(LIR_sub, counter, JSOW_SCRIPT_JUMP);
lir->insStorei(counter, cx_ins,
offsetof(JSContext, operationCount));
}
guard(false, lir->ins2i(LIR_le, counter, 0), snapshot(TIMEOUT_EXIT));
}
/* If we are attached to a tree call guard, make sure the guard the inner tree exited from
@ -3748,8 +3752,12 @@ js_ExecuteTree(JSContext* cx, Fragment* f, uintN& inlineCallCount,
JS_ASSERT(!fp->callee || fp->thisp == JSVAL_TO_OBJECT(fp->argv[-1]));
}
#endif
AUDIT(sideExitIntoInterpreter);
#ifdef JS_JIT_SPEW
if (innermost->exitType != TIMEOUT_EXIT)
AUDIT(sideExitIntoInterpreter);
else
AUDIT(timeoutIntoInterpreter);
#endif
return innermost;
}
@ -4561,8 +4569,7 @@ TraceRecorder::incProp(jsint incr, bool pre)
if (!inc(v, v_ins, incr, pre))
return false;
if (!box_jsval(v, v_ins))
return false;
box_jsval(v, v_ins);
LIns* dslots_ins = NULL;
stobj_set_slot(obj_ins, slot, dslots_ins, v_ins);
@ -4583,8 +4590,7 @@ TraceRecorder::incElem(jsint incr, bool pre)
return false;
if (!inc(*vp, v_ins, incr, pre))
return false;
if (!box_jsval(*vp, v_ins))
return false;
box_jsval(*vp, v_ins);
lir->insStorei(v_ins, addr_ins, 0);
return true;
}
@ -5119,7 +5125,7 @@ TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2
LIns* shape_ins = addName(lir->insLoad(LIR_ld, map_ins, offsetof(JSScope, shape)),
"shape");
guard(true, addName(lir->ins2i(LIR_eq, shape_ins, entry->kshape), "guard(kshape)"),
MISMATCH_EXIT);
BRANCH_EXIT);
}
} else {
#ifdef DEBUG
@ -5132,7 +5138,7 @@ TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2
#endif
if (aobj != globalObj && !obj_ins->isconstp()) {
guard(true, addName(lir->ins2i(LIR_eq, obj_ins, entry->kshape), "guard(kobj)"),
MISMATCH_EXIT);
BRANCH_EXIT);
}
}
@ -5147,7 +5153,7 @@ TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2
if (PCVCAP_TAG(entry->vcap) == 1) {
// Duplicate the special case in PROPERTY_CACHE_TEST.
obj2_ins = stobj_get_fslot(obj_ins, JSSLOT_PROTO);
guard(false, lir->ins_eq0(obj2_ins), MISMATCH_EXIT);
guard(false, lir->ins_eq0(obj2_ins), BRANCH_EXIT);
} else {
obj2_ins = INS_CONSTPTR(obj2);
}
@ -5159,7 +5165,7 @@ TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2
"shape");
guard(true,
addName(lir->ins2i(LIR_eq, shape_ins, vshape), "guard(vshape)"),
MISMATCH_EXIT);
BRANCH_EXIT);
}
pcval = entry->vword;
@ -5278,7 +5284,7 @@ TraceRecorder::native_get(LIns* obj_ins, LIns* pobj_ins, JSScopeProperty* sprop,
// So box_jsval can emit no LIR_or at all to tag an object jsval.
JS_STATIC_ASSERT(JSVAL_OBJECT == 0);
JS_REQUIRES_STACK bool
JS_REQUIRES_STACK void
TraceRecorder::box_jsval(jsval v, LIns*& v_ins)
{
if (isNumber(v)) {
@ -5286,22 +5292,22 @@ TraceRecorder::box_jsval(jsval v, LIns*& v_ins)
v_ins = lir->insCall(&js_BoxDouble_ci, args);
guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_ERROR_COOKIE)),
OOM_EXIT);
return true;
return;
}
switch (JSVAL_TAG(v)) {
case JSVAL_BOOLEAN:
v_ins = lir->ins2i(LIR_pior, lir->ins2i(LIR_pilsh, v_ins, JSVAL_TAGBITS), JSVAL_BOOLEAN);
return true;
return;
case JSVAL_OBJECT:
return true;
case JSVAL_STRING:
return;
default:
JS_ASSERT(JSVAL_TAG(v) == JSVAL_STRING);
v_ins = lir->ins2(LIR_pior, v_ins, INS_CONST(JSVAL_STRING));
return true;
return;
}
return false;
}
JS_REQUIRES_STACK bool
JS_REQUIRES_STACK void
TraceRecorder::unbox_jsval(jsval v, LIns*& v_ins)
{
if (isNumber(v)) {
@ -5316,7 +5322,7 @@ TraceRecorder::unbox_jsval(jsval v, LIns*& v_ins)
MISMATCH_EXIT);
LIns* args[] = { v_ins };
v_ins = lir->insCall(&js_UnboxDouble_ci, args);
return true;
return;
}
switch (JSVAL_TAG(v)) {
case JSVAL_BOOLEAN:
@ -5326,7 +5332,7 @@ TraceRecorder::unbox_jsval(jsval v, LIns*& v_ins)
JSVAL_BOOLEAN),
MISMATCH_EXIT);
v_ins = lir->ins2i(LIR_ush, v_ins, JSVAL_TAGBITS);
return true;
return;
case JSVAL_OBJECT:
if (JSVAL_IS_NULL(v)) {
// JSVAL_NULL maps to type JSVAL_TNULL, so insist that v_ins == 0 here.
@ -5341,18 +5347,17 @@ TraceRecorder::unbox_jsval(jsval v, LIns*& v_ins)
exit);
guard(false, lir->ins_eq0(v_ins), exit);
}
return true;
case JSVAL_STRING:
return;
default:
JS_ASSERT(JSVAL_TAG(v) == JSVAL_STRING);
guard(true,
lir->ins2i(LIR_eq,
lir->ins2(LIR_piand, v_ins, INS_CONST(JSVAL_TAGMASK)),
JSVAL_STRING),
MISMATCH_EXIT);
v_ins = lir->ins2(LIR_piand, v_ins, INS_CONST(~JSVAL_TAGMASK));
return true;
return;
}
JS_NOT_REACHED("unbox_jsval");
return false;
}
JS_REQUIRES_STACK bool
@ -5493,7 +5498,7 @@ TraceRecorder::guardElemOp(JSObject* obj, LIns* obj_ins, jsid id, size_t op_offs
ABORT_TRACE("resolve hook mutated elem op base object");
LIns* shape_ins = addName(lir->insLoad(LIR_ld, map_ins, offsetof(JSScope, shape)), "shape");
guard(true, addName(lir->ins2i(LIR_eq, shape_ins, shape), "guard(shape)"), MISMATCH_EXIT);
guard(true, addName(lir->ins2i(LIR_eq, shape_ins, shape), "guard(shape)"), BRANCH_EXIT);
return true;
}
@ -5585,8 +5590,7 @@ TraceRecorder::record_JSOP_POPV()
{
jsval& rval = stackval(-1);
LIns *rval_ins = get(&rval);
if (!box_jsval(rval, rval_ins))
return false;
box_jsval(rval, rval_ins);
// Store it in cx->fp->rval. NB: Tricky dependencies. cx->fp is the right
// frame because POPV appears only in global and eval code and we don't
@ -6023,8 +6027,7 @@ TraceRecorder::newArray(JSObject *ctor, uint32 argc, jsval *argv, jsval *rval)
LIns *dslots_ins = NULL;
for (uint32 i = 0; i < argc; i++) {
LIns *elt_ins = get(argv + i);
if (!box_jsval(argv[i], elt_ins))
return false;
box_jsval(argv[i], elt_ins);
stobj_set_dslot(arr_ins, i, dslots_ins, elt_ins, "set_array_elt");
}
}
@ -6043,7 +6046,7 @@ TraceRecorder::functionCall(bool constructing, uintN argc)
if (!VALUE_IS_FUNCTION(cx, fval))
ABORT_TRACE("callee is not a function");
jsval& tval = stackval(0 - (argc + 1));
LIns* this_ins = get(&tval);
@ -6160,8 +6163,7 @@ TraceRecorder::functionCall(bool constructing, uintN argc)
if (!VALUE_IS_FUNCTION(cx, arg))
goto next_specialization;
} else if (argtype == 'v') {
if (!box_jsval(arg, *argp))
return false;
box_jsval(arg, *argp);
} else {
goto next_specialization;
}
@ -6442,7 +6444,7 @@ TraceRecorder::record_SetPropHit(JSPropCacheEntry* entry, JSScopeProperty* sprop
LIns* shape_ins = addName(lir->insLoad(LIR_ld, map_ins, offsetof(JSScope, shape)), "shape");
guard(true, addName(lir->ins2i(LIR_eq, shape_ins, entry->kshape), "guard(shape)"),
MISMATCH_EXIT);
BRANCH_EXIT);
if (entry->kshape != PCVCAP_SHAPE(entry->vcap)) {
LIns* args[] = { INS_CONSTPTR(sprop), obj_ins, cx_ins };
@ -6453,8 +6455,7 @@ TraceRecorder::record_SetPropHit(JSPropCacheEntry* entry, JSScopeProperty* sprop
LIns* dslots_ins = NULL;
LIns* v_ins = get(&r);
LIns* boxed_ins = v_ins;
if (!box_jsval(r, boxed_ins))
return false;
box_jsval(r, boxed_ins);
if (!native_set(obj_ins, sprop, dslots_ins, boxed_ins))
return false;
@ -6530,8 +6531,7 @@ TraceRecorder::record_JSOP_GETELEM()
LIns* args[] = { idx_ins, obj_ins, cx_ins };
v_ins = lir->insCall(&js_Any_getprop_ci, args);
guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_ERROR_COOKIE)), MISMATCH_EXIT);
if (!unbox_jsval(v, v_ins))
ABORT_TRACE("JSOP_GETELEM");
unbox_jsval(v, v_ins);
set(&lval, v_ins);
return true;
}
@ -6553,8 +6553,7 @@ TraceRecorder::record_JSOP_GETELEM()
return false;
LIns* v_ins = lir->insCall(&js_Any_getelem_ci, args);
guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_ERROR_COOKIE)), MISMATCH_EXIT);
if (!unbox_jsval(v, v_ins))
ABORT_TRACE("JSOP_GETELEM");
unbox_jsval(v, v_ins);
set(&lval, v_ins);
return true;
}
@ -6585,8 +6584,7 @@ TraceRecorder::record_JSOP_SETELEM()
jsid id;
LIns* boxed_v_ins = v_ins;
if (!box_jsval(v, boxed_v_ins))
ABORT_TRACE("boxing JSOP_SETELEM value");
box_jsval(v, boxed_v_ins);
if (JSVAL_IS_STRING(idx)) {
if (!js_ValueToStringId(cx, idx, &id))
@ -6669,6 +6667,8 @@ TraceRecorder::record_JSOP_CALLUPVAR()
JS_REQUIRES_STACK bool
TraceRecorder::guardCallee(jsval& callee)
{
JS_ASSERT(VALUE_IS_FUNCTION(cx, callee));
LIns* exit = snapshot(BRANCH_EXIT);
JSObject* callee_obj = JSVAL_TO_OBJECT(callee);
LIns* callee_ins = get(&callee);
@ -6801,10 +6801,12 @@ TraceRecorder::record_JSOP_APPLY()
*/
if (argc > 0 && JSVAL_IS_PRIMITIVE(vp[2]))
return record_JSOP_CALL();
/*
* Guard on the identity of this, which is the function we are applying.
*/
if (!VALUE_IS_FUNCTION(cx, vp[1]))
ABORT_TRACE("callee is not a function");
if (!guardCallee(vp[1]))
return false;
@ -6874,9 +6876,8 @@ TraceRecorder::record_FastNativeCallComplete()
bool ok = true;
switch (JSTN_ERRTYPE(pendingTraceableNative)) {
case FAIL_JSVAL:
ok = unbox_jsval(v, v_ins);
if (ok)
set(&v, v_ins);
unbox_jsval(v, v_ins);
set(&v, v_ins);
break;
case FAIL_NEG:
/* Already added i2f in functionCall. */
@ -6984,10 +6985,8 @@ TraceRecorder::prop(JSObject* obj, LIns* obj_ins, uint32& slot, LIns*& v_ins)
LIns* args[] = { INS_CONSTPTR(sprop), obj_ins, cx_ins };
v_ins = lir->insCall(&js_CallGetter_ci, args);
guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_ERROR_COOKIE)), OOM_EXIT);
if (!unbox_jsval((sprop->shortid == REGEXP_SOURCE) ? JSVAL_STRING : JSVAL_BOOLEAN,
v_ins)) {
ABORT_TRACE("unboxing");
}
unbox_jsval((sprop->shortid == REGEXP_SOURCE) ? JSVAL_STRING : JSVAL_BOOLEAN,
v_ins);
JS_ASSERT(cs.ndefs == 1);
stack(-cs.nuses, v_ins);
return true;
@ -7004,8 +7003,7 @@ TraceRecorder::prop(JSObject* obj, LIns* obj_ins, uint32& slot, LIns*& v_ins)
}
v_ins = stobj_get_slot(obj_ins, slot, dslots_ins);
if (!unbox_jsval(STOBJ_GET_SLOT(obj, slot), v_ins))
ABORT_TRACE("unboxing");
unbox_jsval(STOBJ_GET_SLOT(obj, slot), v_ins);
return true;
}
@ -7051,8 +7049,7 @@ TraceRecorder::elem(jsval& oval, jsval& idx, jsval*& vp, LIns*& v_ins, LIns*& ad
/* Load the value and guard on its type to unbox it. */
v_ins = lir->insLoad(LIR_ldp, addr_ins, 0);
if (!unbox_jsval(*vp, v_ins))
return false;
unbox_jsval(*vp, v_ins);
if (JSVAL_TAG(*vp) == JSVAL_BOOLEAN) {
// Optimize to guard for a hole only after untagging, so we know that
@ -7295,18 +7292,6 @@ TraceRecorder::record_JSOP_ENDINIT()
{
jsval& v = stackval(-1);
JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
JSObject* obj = JSVAL_TO_OBJECT(v);
if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
// Until we get JSOP_NEWARRAY working, we do our optimizing here...
if (obj->fslots[JSSLOT_ARRAY_LENGTH] == 1 &&
obj->dslots && JSVAL_IS_STRING(obj->dslots[0])) {
LIns* v_ins = get(&v);
JS_ASSERT(v_ins->isCall() && v_ins->callInfo() == &js_FastNewArray_ci);
LIns* args[] = { stack(1), callArgN(v_ins, 1), cx_ins };
v_ins = lir->insCall(&js_Array_1str_ci, args);
set(&v, v_ins);
}
}
return true;
}
@ -7495,11 +7480,9 @@ TraceRecorder::record_IteratorNextComplete()
jsval& v = stackval(-2);
LIns* v_ins = get(&v);
if (unbox_jsval(v, v_ins)) {
set(&v, v_ins);
return true;
}
return false;
unbox_jsval(v, v_ins);
set(&v, v_ins);
return true;
}
JS_REQUIRES_STACK bool
@ -8502,7 +8485,7 @@ TraceRecorder::record_JSOP_CALLGVAR()
if (!lazilyImportGlobalSlot(slot))
ABORT_TRACE("lazy import of global slot failed");
jsval& v = STOBJ_GET_SLOT(cx->fp->scopeChain, slot);
jsval& v = STOBJ_GET_SLOT(globalObj, slot);
stack(0, get(&v));
stack(1, INS_CONSTPTR(NULL));
return true;
@ -8595,7 +8578,33 @@ TraceRecorder::record_JSOP_LENGTH()
JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_NEWARRAY()
{
return false;
JSObject* proto;
const CallInfo* ci = &js_NewUninitializedArray_ci;
if (!js_GetClassPrototype(cx, globalObj, INT_TO_JSID(JSProto_Array), &proto))
return false;
uint32 len = GET_UINT24(cx->fp->regs->pc);
LIns* args[] = { lir->insImm(len), INS_CONSTPTR(proto), cx_ins };
LIns* v_ins = lir->insCall(ci, args);
guard(false, lir->ins_eq0(v_ins), OOM_EXIT);
// De-optimize when we might have setters on Array.prototype.
LIns* rt_ins = lir->insLoad(LIR_ldp, cx_ins, offsetof(JSContext, runtime));
guard(true,
lir->ins_eq0(lir->insLoad(LIR_ldp, rt_ins,
offsetof(JSRuntime, anyArrayProtoHasElement))),
MISMATCH_EXIT);
LIns* dslots_ins = NULL;
for (uint32 i = 0; i < len; i++) {
jsval& v = stackval(-len + i);
LIns* elt_ins = get(&v);
box_jsval(v, elt_ins);
stobj_set_dslot(v_ins, i, dslots_ins, elt_ins, "set_array_elt");
}
stack(-len, v_ins);
return true;
}
JS_REQUIRES_STACK bool

View File

@ -79,9 +79,10 @@ public:
}
bool contains(T a) {
for (unsigned n = 0; n < _len; ++n)
for (unsigned n = 0; n < _len; ++n) {
if (_data[n] == a)
return true;
}
return false;
}
@ -414,8 +415,8 @@ class TraceRecorder : public avmplus::GCObject {
JS_REQUIRES_STACK bool getProp(jsval& v);
JS_REQUIRES_STACK bool getThis(nanojit::LIns*& this_ins);
JS_REQUIRES_STACK bool box_jsval(jsval v, nanojit::LIns*& v_ins);
JS_REQUIRES_STACK bool unbox_jsval(jsval v, nanojit::LIns*& v_ins);
JS_REQUIRES_STACK void box_jsval(jsval v, nanojit::LIns*& v_ins);
JS_REQUIRES_STACK void unbox_jsval(jsval v, nanojit::LIns*& v_ins);
JS_REQUIRES_STACK bool guardClass(JSObject* obj, nanojit::LIns* obj_ins, JSClass* clasp,
ExitType exitType = MISMATCH_EXIT);
JS_REQUIRES_STACK bool guardDenseArray(JSObject* obj, nanojit::LIns* obj_ins,

View File

@ -1127,11 +1127,11 @@ namespace nanojit
NanoAssert(argc <= (int)MAXARGS);
uint32_t words = argwords(argc);
int32_t insSz = words + LIR_CALL_SLOTS; // words need for offsets + size of instruction
ensureRoom(argc + insSz); // argc=# possible tramps for args
ensureRoom(argc * LIR_FAR_SLOTS + insSz); // argc=# possible tramps for args
// Argument deltas are calculated relative to the final LIns,
// which is the last word in the cluster.
LInsp from = _buf->next() + argc + insSz - 1;
LInsp from = _buf->next() + argc * LIR_FAR_SLOTS + insSz - 1;
for (int32_t i=0; i < argc; i++)
makeReachable(args[i], from);

View File

@ -106,7 +106,8 @@ namespace nanojit
uint32_t stackPushed =
STACK_GRANULARITY + // returnaddr
STACK_GRANULARITY; // ebp
STACK_GRANULARITY + // ebp
STACK_GRANULARITY; // dummy
if (!_thisfrag->lirbuf->explicitSavedRegs)
stackPushed += NumSavedRegs * STACK_GRANULARITY;
@ -130,9 +131,11 @@ namespace nanojit
MR(FP, SP); // Establish our own FP.
PUSHr(FP); // Save caller's FP.
if (!_thisfrag->lirbuf->explicitSavedRegs)
if (!_thisfrag->lirbuf->explicitSavedRegs) {
PUSHr(FP); // dummy
for (int i = 0; i < NumSavedRegs; ++i)
PUSHr(savedRegs[i]);
}
// align the entry point
asm_align_code();
@ -207,9 +210,11 @@ namespace nanojit
{
RET();
if (!_thisfrag->lirbuf->explicitSavedRegs)
if (!_thisfrag->lirbuf->explicitSavedRegs) {
for (int i = NumSavedRegs - 1; i >= 0; --i)
POPr(savedRegs[i]);
POPr(FP); // dummy
}
POPr(FP); // Restore caller's FP.
MR(SP,FP); // pop the stack frame

View File

@ -107,6 +107,16 @@ static size_t gScriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;
static jsdouble gOperationTimeout = -1.0;
/*
* Watchdog thread state.
*/
#ifdef JS_THREADSAFE
static PRCondVar *gWatchdogWakeup;
static PRThread *gWatchdogThread;
static PRIntervalTime gWatchdogSleepDuration = 0;
static PRIntervalTime gLastWatchdogWakeup;
#endif
int gExitCode = 0;
JSBool gQuitting = JS_FALSE;
FILE *gErrFile = NULL;
@ -129,12 +139,6 @@ my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber);
static JSObject *
split_setup(JSContext *cx);
static JSBool
SetTimeoutValue(JSContext *cx, jsdouble t);
static void
RescheduleOperationCallback(JSContext *cx);
#ifdef EDITLINE
JS_BEGIN_EXTERN_C
extern char *readline(const char *prompt);
@ -142,8 +146,11 @@ extern void add_history(char *line);
JS_END_EXTERN_C
#endif
static JSBool
GetLine(JSContext *cx, char *bufp, FILE *file, const char *prompt) {
static char *
GetLine(FILE *file, const char * prompt)
{
size_t size;
char *buffer;
#ifdef EDITLINE
/*
* Use readline only if file is stdin, because there's no way to specify
@ -151,62 +158,116 @@ GetLine(JSContext *cx, char *bufp, FILE *file, const char *prompt) {
*/
if (file == stdin) {
char *linep = readline(prompt);
/*
* We set it to zero to avoid complaining about inappropriate ioctl
* for device in the case of EOF. Looks like errno == 251 if line is
* finished with EOF and errno == 25 if there is nothing left
* to read.
*/
if (errno == 251 || errno == 25)
errno = 0;
if (!linep)
return JS_FALSE;
return NULL;
if (linep[0] != '\0')
add_history(linep);
strcpy(bufp, linep);
JS_free(cx, linep);
bufp += strlen(bufp);
*bufp++ = '\n';
*bufp = '\0';
} else
return linep;
}
#endif
{
char line[256];
size_t len = 0;
if (*prompt != '\0') {
fprintf(gOutFile, prompt);
fflush(gOutFile);
if (!fgets(line, sizeof line, file))
return JS_FALSE;
strcpy(bufp, line);
}
return JS_TRUE;
}
/* Time-related portability helpers. */
typedef int64 OperationTime;
const OperationTime TIME_INFINITY = -1LL;
static inline OperationTime
TicksPerSecond()
{
return 1000LL * 1000LL;
}
static inline OperationTime
MaybeGCPeriod()
{
return TicksPerSecond() / 10;
}
static inline OperationTime
YieldRequestPeriod()
{
return TicksPerSecond() / 50;
size = 80;
buffer = (char *) malloc(size);
if (!buffer)
return NULL;
char *current = buffer;
while (fgets(current, size - len, file)) {
len += strlen(current);
char *t = buffer + len - 1;
if (*t == '\n') {
/* Line was read. We remove '\n' and exit. */
*t = '\0';
return buffer;
}
if (len + 1 == size) {
size = size * 2;
char *tmp = (char *) realloc(buffer, size);
if (!tmp) {
free(buffer);
return NULL;
}
buffer = tmp;
}
current = buffer + len;
}
if (len && !ferror(file))
return buffer;
free(buffer);
return NULL;
}
/*
* State to store as JSContext private.
*
* In the JS_THREADSAFE case, when the watchdog thread triggers the operation
* callback, we use PR_IntervalNow(), not JS_Now() as the latter could be
* expensive and is not suitable for calls when a GC lock is held. This forces
* us to use PRIntervalTime as a time type and deal with potential time-wraps
* over uint32 limit. In particular, we must use time relative to some recent
* timestamp when checking for expiration, not absolute time values, as in the
* !JS_THREADSAFE case, when time is int64 and no time-wraps are feasible.
*
* We declare such timestamps as volatile as they are updated in the operation
* callback without taking any locks. Any possible race can only lead to more
* frequent callback calls. This is safe as the callback does everything based
* on timing.
*/
struct JSShellContextData {
OperationTime lastCallbackTime; /* the last operation callback call
time */
OperationTime operationTimeout; /* time left for script execution */
OperationTime lastMaybeGCTime; /* the last JS_MaybeGC call time */
OperationTime maybeGCPeriod; /* period of JS_MaybeGC calls */
#ifdef JS_THREADSAFE
OperationTime lastYieldTime; /* the last JS_YieldRequest call time */
OperationTime yieldPeriod; /* period of JS_YieldRequest calls */
PRIntervalTime timeout;
volatile PRIntervalTime startTime; /* startTime + timeout is time when
script must be stopped */
PRIntervalTime maybeGCPeriod;
volatile PRIntervalTime lastMaybeGCTime;/* lastMaybeGCTime + maybeGCPeriod
is the time to call MaybeGC */
PRIntervalTime yieldPeriod;
volatile PRIntervalTime lastYieldTime; /* lastYieldTime + yieldPeriod is
the time to call
JS_YieldRequest() */
#else
int64 stopTime; /* time when script must be
stopped */
int64 nextMaybeGCTime;/* time to call JS_MaybeGC */
#endif
};
static JSBool
SetTimeoutValue(JSContext *cx, jsdouble t);
#ifdef JS_THREADSAFE
# define DEFAULT_YIELD_PERIOD() (PR_TicksPerSecond() / 50)
# define DEFAULT_MAYBEGC_PERIOD() (PR_TicksPerSecond() / 10)
/*
* The function assumes that the GC lock is already held on entry. On a
* successful exit the lock will be held, on failure the lock is released and
* the error is reported.
*/
static JSBool
RescheduleWatchdog(JSContext *cx, JSShellContextData *data, PRIntervalTime now);
#else
# define DEFAULT_MAYBEGC_PERIOD() (MICROSECONDS_PER_SECOND / 10)
const int64 MICROSECONDS_PER_SECOND = 1000000LL;
const int64 MAX_TIME_VALUE = 0x7FFFFFFFFFFFFFFFLL;
#endif
static JSShellContextData *
NewContextData()
{
@ -214,14 +275,20 @@ NewContextData()
malloc(sizeof(JSShellContextData));
if (!data)
return NULL;
data->lastCallbackTime = 0;
data->operationTimeout = TIME_INFINITY;
data->lastMaybeGCTime = 0;
data->maybeGCPeriod = TIME_INFINITY;
#ifdef JS_THREADSAFE
data->timeout = PR_INTERVAL_NO_TIMEOUT;
data->maybeGCPeriod = PR_INTERVAL_NO_TIMEOUT;
data->yieldPeriod = PR_INTERVAL_NO_TIMEOUT;
# ifdef DEBUG
data->startTime = 0;
data->lastMaybeGCTime = 0;
data->lastYieldTime = 0;
data->yieldPeriod = TIME_INFINITY;
# endif
#else /* !JS_THREADSAFE */
data->stopTime = MAX_TIME_VALUE;
data->nextMaybeGCTime = MAX_TIME_VALUE;
#endif
return data;
}
@ -238,30 +305,45 @@ static JSBool
ShellOperationCallback(JSContext *cx)
{
JSShellContextData *data = GetContextData(cx);
OperationTime now = JS_Now();
OperationTime interval = now - data->lastCallbackTime;
data->lastCallbackTime = now;
if (data->operationTimeout != TIME_INFINITY) {
if (data->operationTimeout < interval) {
fprintf(stderr, "Error: Script is running too long\n");
return JS_FALSE;
}
data->operationTimeout -= interval;
}
if (data->maybeGCPeriod != TIME_INFINITY) {
if (now - data->lastMaybeGCTime >= data->maybeGCPeriod) {
JS_MaybeGC(cx);
data->lastMaybeGCTime = now;
}
}
JSBool doStop;
JSBool doMaybeGC;
#ifdef JS_THREADSAFE
if (data->yieldPeriod != TIME_INFINITY) {
if (now - data->lastYieldTime >= data->yieldPeriod) {
JS_YieldRequest(cx);
data->lastYieldTime = now;
}
JSBool doYield;
PRIntervalTime now = PR_IntervalNow();
doStop = (data->timeout != PR_INTERVAL_NO_TIMEOUT &&
now - data->startTime >= data->timeout);
doMaybeGC = (data->maybeGCPeriod != PR_INTERVAL_NO_TIMEOUT &&
now - data->lastMaybeGCTime >= data->maybeGCPeriod);
if (doMaybeGC)
data->lastMaybeGCTime = now;
doYield = (data->yieldPeriod != PR_INTERVAL_NO_TIMEOUT &&
now - data->lastYieldTime >= data->yieldPeriod);
if (doYield)
data->lastYieldTime = now;
#else /* !JS_THREADSAFE */
int64 now = JS_Now();
doStop = (now >= data->stopTime);
doMaybeGC = (now >= data->nextMaybeGCTime);
if (doMaybeGC)
data->nextMaybeGCTime = now + DEFAULT_MAYBEGC_PERIOD();
#endif
if (doStop) {
fputs("Error: script is running for too long\n", stderr);
return JS_FALSE;
}
if (doMaybeGC)
JS_MaybeGC(cx);
#ifdef JS_THREADSAFE
if (doYield)
JS_YieldRequest(cx);
#endif
return JS_TRUE;
@ -287,6 +369,7 @@ SetContextOptions(JSContext *cx)
JS_SetThreadStackLimit(cx, stackLimit);
JS_SetScriptStackQuota(cx, gScriptStackQuota);
SetTimeoutValue(cx, gOperationTimeout);
JS_SetOperationCallbackFunction(cx, ShellOperationCallback);
}
static void
@ -296,8 +379,8 @@ Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY)
JSScript *script;
jsval result;
JSString *str;
char buffer[4096];
char *bufp;
char *buffer;
size_t size;
int lineno;
int startline;
FILE *file;
@ -356,10 +439,9 @@ Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY)
/* It's an interactive filehandle; drop into read-eval-print loop. */
lineno = 1;
hitEOF = JS_FALSE;
size_t len;
buffer = NULL;
do {
bufp = buffer;
*bufp = '\0';
/*
* Accumulate lines until we get a 'compilable unit' - one that either
* generates an error (before running out of source) or that compiles
@ -368,17 +450,53 @@ Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY)
*/
startline = lineno;
do {
if (!GetLine(cx, bufp, file, startline == lineno ? "js> " : "")) {
errno = 0;
char *line = GetLine(file, startline == lineno ? "js> " : "");
if (!line) {
if (errno) {
JS_ReportError(cx, strerror(errno));
free(buffer);
return;
}
hitEOF = JS_TRUE;
break;
}
bufp += strlen(bufp);
if (!buffer) {
buffer = line;
len = strlen(buffer);
size = len + 1;
} else {
/**
* len + 1 is required to store '\n' in the end of line
*/
size_t newlen = strlen(line) + (len ? len + 1 : 0);
if (newlen + 1 > size) {
size = newlen + 1 > size * 2 ? newlen + 1 : size * 2;
char *newBuf = (char *) realloc(buffer, size);
if (!newBuf) {
free(buffer);
free(line);
JS_ReportOutOfMemory(cx);
return;
}
buffer = newBuf;
}
char *current = buffer + len;
if (startline != lineno)
*current++ = '\n';
strcpy(current, line);
len = newlen;
free(line);
}
lineno++;
} while (!JS_BufferIsCompilableUnit(cx, obj, buffer, strlen(buffer)));
} while (!JS_BufferIsCompilableUnit(cx, obj, buffer, len));
if (hitEOF)
break;
/* Clear any pending exception from previous failed compiles. */
JS_ClearPendingException(cx);
script = JS_CompileScript(cx, obj, buffer, strlen(buffer), "typein",
script = JS_CompileScript(cx, obj, buffer, len, "typein",
startline);
if (script) {
if (!compileOnly) {
@ -393,7 +511,11 @@ Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY)
}
JS_DestroyScript(cx, script);
}
} while (!hitEOF && !gQuitting);
*buffer = '\0';
len = 0;
} while (!gQuitting);
free(buffer);
fprintf(gOutFile, "\n");
if (file != stdin)
fclose(file);
@ -2678,7 +2800,7 @@ out:
#ifdef JS_THREADSAFE
JS_EndRequest(scx);
#endif
JS_DestroyContext(scx);
JS_DestroyContextNoGC(scx);
return ok;
}
@ -2758,19 +2880,40 @@ DoScatteredWork(JSContext *cx, ScatterThreadData *td)
{
jsval *rval = &td->shared->results[td->index];
JSShellContextData *data = (JSShellContextData *) JS_GetContextPrivate(cx);
OperationTime oldPeriod = data->yieldPeriod;
if (oldPeriod == TIME_INFINITY)
data->lastYieldTime = JS_Now();
data->yieldPeriod = YieldRequestPeriod();
RescheduleOperationCallback(cx);
if (!JS_CallFunctionValue(cx, NULL, td->fn, 0, NULL, rval)) {
JSShellContextData *data = GetContextData(cx);
PRIntervalTime oldYieldPeriod = data->yieldPeriod;
PRIntervalTime newYieldPeriod = DEFAULT_YIELD_PERIOD();
JSBool scheduleOk = JS_TRUE;
/*
* Here oldYieldPeriod is DEFAULT_YIELD_PERIOD() when the scatter reuses
* a context used by a previous scatter call.
*/
if (oldYieldPeriod != newYieldPeriod) {
JS_LOCK_GC(cx->runtime);
PRIntervalTime now = PR_IntervalNow();
JS_ASSERT(oldYieldPeriod == PR_INTERVAL_NO_TIMEOUT);
data->lastYieldTime = now;
data->yieldPeriod = newYieldPeriod;
scheduleOk = RescheduleWatchdog(cx, data, now);
if (scheduleOk)
JS_UNLOCK_GC(cx->runtime);
}
if (!scheduleOk ||
!JS_CallFunctionValue(cx, NULL, td->fn, 0, NULL, rval)) {
*rval = JSVAL_VOID;
JS_GetPendingException(cx, rval);
JS_ClearPendingException(cx);
}
data->yieldPeriod = oldPeriod;
RescheduleOperationCallback(cx);
/*
* We do not need to lock or call RescheduleWatchdog. Here yieldPeriod
* can only stay at DEFAULT_YIELD_PERIOD or go to PR_INTERVAL_NO_TIMEOUT.
* Thus we never need to wake up the watchdog thread earlier.
*/
JS_ASSERT(oldYieldPeriod == data->yieldPeriod ||
oldYieldPeriod == PR_INTERVAL_NO_TIMEOUT);
data->yieldPeriod = oldYieldPeriod;
}
static void
@ -2964,55 +3107,189 @@ fail:
ok = JS_FALSE;
goto out;
}
#endif /* JS_THREADSAFE */
/*
* Find duration between now and base + period, set it to sleepDuration if the
* latter value is greater and set expired to true if base + period comes
* before now. This function correctly deals with a possible time wrap between
* base and now.
*/
static void
UpdateSleepDuration(PRIntervalTime now, PRIntervalTime base,
PRIntervalTime period, PRIntervalTime &sleepDuration,
JSBool &expired)
{
if (period == PR_INTERVAL_NO_TIMEOUT)
return;
PRIntervalTime t;
PRIntervalTime diff = now - base;
if (diff >= period) {
expired = JS_TRUE;
t = period;
} else {
t = period - diff;
}
if (sleepDuration == PR_INTERVAL_NO_TIMEOUT || sleepDuration > t)
sleepDuration = t;
}
static void
RescheduleOperationCallback(JSContext *cx)
CheckCallbackTime(JSContext *cx, JSShellContextData *data, PRIntervalTime now,
PRIntervalTime &sleepDuration)
{
JSShellContextData *data = GetContextData(cx);
JSBool expired = JS_FALSE;
if (data->operationTimeout == TIME_INFINITY &&
#ifdef JS_THREADSAFE
data->yieldPeriod == TIME_INFINITY &&
#endif
data->maybeGCPeriod == TIME_INFINITY) {
JS_ClearOperationCallback(cx);
} else if (!JS_GetOperationCallback(cx)) {
/*
* Call the callback infrequently enough to avoid the overhead of time
* calculations there.
*/
JS_SetOperationCallback(cx, ShellOperationCallback,
1000 * JS_OPERATION_WEIGHT_BASE);
data->lastCallbackTime = JS_Now();
UpdateSleepDuration(now, data->startTime, data->timeout,
sleepDuration, expired);
UpdateSleepDuration(now, data->lastMaybeGCTime, data->maybeGCPeriod,
sleepDuration, expired);
UpdateSleepDuration(now, data->lastYieldTime, data->yieldPeriod,
sleepDuration, expired);
if (expired) {
JS_ASSERT(sleepDuration != PR_INTERVAL_NO_TIMEOUT);
JS_TriggerOperationCallback(cx);
}
}
static void
WatchdogMain(void *arg)
{
JSRuntime *rt = (JSRuntime *) arg;
PRBool isRunning = JS_TRUE;
JS_LOCK_GC(rt);
while (gWatchdogThread) {
PRIntervalTime now = PR_IntervalNow();
PRIntervalTime sleepDuration = PR_INTERVAL_NO_TIMEOUT;
JSContext *iter = NULL;
JSContext *acx;
while ((acx = js_ContextIterator(rt, JS_FALSE, &iter))) {
if (acx->requestDepth > 0) {
JSShellContextData *data = (JSShellContextData *)
JS_GetContextPrivate(acx);
/*
* For the last context inside JS_DestroyContext the engine
* starts a new request to shutdown the runtime. For such
* context data is null.
*/
if (data)
CheckCallbackTime(acx, data, now, sleepDuration);
}
}
gLastWatchdogWakeup = now;
gWatchdogSleepDuration = sleepDuration;
#ifdef DEBUG
PRStatus status =
#endif
PR_WaitCondVar(gWatchdogWakeup, sleepDuration);
JS_ASSERT(status == PR_SUCCESS);
}
/* Wake up the main thread waiting for the watchdog to terminate. */
PR_NotifyCondVar(gWatchdogWakeup);
JS_UNLOCK_GC(rt);
}
static JSBool
RescheduleWatchdog(JSContext *cx, JSShellContextData *data, PRIntervalTime now)
{
JS_ASSERT(data == GetContextData(cx));
PRIntervalTime nextCallbackTime = PR_INTERVAL_NO_TIMEOUT;
CheckCallbackTime(cx, data, now, nextCallbackTime);
if (nextCallbackTime == PR_INTERVAL_NO_TIMEOUT)
return JS_TRUE;
if (gWatchdogThread) {
/*
* Notify the watchdog if it would wake up after data->watchdogLimit
* expires. PRIntervalTime is unsigned so the subtraction in the
* following check gives the correct interval even when time wraps
* around between gLastWatchdogWakeup and now.
*/
if (gWatchdogSleepDuration == PR_INTERVAL_NO_TIMEOUT ||
PRInt32(now - gLastWatchdogWakeup) <
PRInt32(gWatchdogSleepDuration) - PRInt32(nextCallbackTime)) {
PR_NotifyCondVar(gWatchdogWakeup);
}
} else {
gWatchdogThread = PR_CreateThread(PR_USER_THREAD,
WatchdogMain,
cx->runtime,
PR_PRIORITY_NORMAL,
PR_LOCAL_THREAD,
PR_UNJOINABLE_THREAD,
0);
if (!gWatchdogThread) {
JS_UNLOCK_GC(cx->runtime);
JS_ReportError(cx, "failed to create the watchdog thread");
return JS_FALSE;
}
/* The watchdog thread does not sleep on creation. */
JS_ASSERT(gWatchdogSleepDuration == 0);
gLastWatchdogWakeup = now;
}
return JS_TRUE;
}
#endif /* JS_THREADSAFE */
static JSBool
SetTimeoutValue(JSContext *cx, jsdouble t)
{
/* NB: The next condition also filter out NaNs. */
if (!(t <= 3600.0)) {
JS_ReportError(cx, "Excessive argument value");
JS_ReportError(cx, "Excessive timeout value");
return JS_FALSE;
}
JSShellContextData *data = GetContextData(cx);
/*
* For compatibility periodic MaybeGC calls are enabled only when the
* execution time is bounded.
*/
JSShellContextData *data = GetContextData(cx);
#ifdef JS_THREADSAFE
JS_LOCK_GC(cx->runtime);
if (t < 0) {
data->operationTimeout = TIME_INFINITY;
data->maybeGCPeriod = TIME_INFINITY;
data->timeout = PR_INTERVAL_NO_TIMEOUT;
data->maybeGCPeriod = PR_INTERVAL_NO_TIMEOUT;
} else {
data->operationTimeout = OperationTime(t * TicksPerSecond());
if (data->maybeGCPeriod == TIME_INFINITY)
data->lastMaybeGCTime = JS_Now();
data->maybeGCPeriod = MaybeGCPeriod();
PRIntervalTime now = PR_IntervalNow();
data->timeout = PRIntervalTime(t * PR_TicksPerSecond());
data->startTime = now;
if (data->maybeGCPeriod == PR_INTERVAL_NO_TIMEOUT) {
data->maybeGCPeriod = DEFAULT_MAYBEGC_PERIOD();
data->lastMaybeGCTime = now;
}
if (!RescheduleWatchdog(cx, data, now)) {
/* The GC lock is already released here. */
return JS_FALSE;
}
}
RescheduleOperationCallback(cx);
JS_UNLOCK_GC(cx->runtime);
#else /* !JS_THREADSAFE */
if (t < 0) {
data->stopTime = MAX_TIME_VALUE;
data->nextMaybeGCTime = MAX_TIME_VALUE;
JS_SetOperationLimit(cx, JS_MAX_OPERATION_LIMIT);
} else {
int64 now = JS_Now();
data->stopTime = now + int64(t * MICROSECONDS_PER_SECOND);
if (data->nextMaybeGCTime == MAX_TIME_VALUE)
data->nextMaybeGCTime = now + DEFAULT_MAYBEGC_PERIOD();
/*
* Call the callback infrequently enough to avoid the overhead of
* time calculations there.
*/
JS_SetOperationLimit(cx, 1000 * JS_OPERATION_WEIGHT_BASE);
}
#endif
return JS_TRUE;
}
@ -3021,11 +3298,28 @@ Timeout(JSContext *cx, uintN argc, jsval *vp)
{
if (argc == 0) {
JSShellContextData *data = GetContextData(cx);
jsdouble t;
t = (data->operationTimeout == TIME_INFINITY)
? -1
: jsdouble(data->operationTimeout) / TicksPerSecond();
return JS_NewDoubleValue(cx, t, vp);
jsdouble t; /* remaining time to run */
#ifdef JS_THREADSAFE
if (data->timeout == PR_INTERVAL_NO_TIMEOUT) {
t = -1.0;
} else {
PRIntervalTime expiredTime = PR_IntervalNow() - data->startTime;
t = (expiredTime >= data->timeout)
? 0.0
: jsdouble(data->timeout - expiredTime) / PR_TicksPerSecond();
}
#else
if (data->stopTime == MAX_TIME_VALUE) {
t = -1.0;
} else {
int64 remainingTime = data->stopTime - JS_Now();
t = (remainingTime <= 0)
? 0.0
: jsdouble(remainingTime) / MICROSECONDS_PER_SECOND;
}
#endif
return JS_NewNumberValue(cx, t, vp);
}
if (argc > 1) {
@ -4158,6 +4452,13 @@ main(int argc, char **argv, char **envp)
rt = JS_NewRuntime(64L * 1024L * 1024L);
if (!rt)
return 1;
#ifdef JS_THREADSAFE
gWatchdogWakeup = JS_NEW_CONDVAR(rt->gcLock);
if (!gWatchdogWakeup)
return 1;
#endif
JS_SetContextCallback(rt, ContextCallback);
cx = JS_NewContext(rt, gStackChunkSize);
@ -4268,6 +4569,22 @@ main(int argc, char **argv, char **envp)
#endif
JS_DestroyContext(cx);
#ifdef JS_THREADSAFE
JS_LOCK_GC(rt);
if (gWatchdogThread) {
/*
* The watchdog thread is running, tell it to terminate waking it up
* if necessary and wait until it signals that it done.
*/
gWatchdogThread = NULL;
PR_NotifyCondVar(gWatchdogWakeup);
PR_WaitCondVar(gWatchdogWakeup, PR_INTERVAL_NO_TIMEOUT);
}
JS_UNLOCK_GC(rt);
JS_DESTROY_CONDVAR(gWatchdogWakeup);
#endif
JS_DestroyRuntime(rt);
JS_ShutDown();
return result;

View File

@ -29,6 +29,7 @@ function jitstatHandler(f)
f("recorderAborted");
f("traceCompleted");
f("sideExitIntoInterpreter");
f("timeoutIntoInterpreter");
f("typeMapMismatchAtEntry");
f("returnToDifferentLoopHeader");
f("traceTriggered");
@ -36,20 +37,36 @@ function jitstatHandler(f)
f("treesTrashed");
f("slotPromoted");
f("unstableLoopVariable");
f("noCompatInnerTrees");
f("breakLoopExits");
f("returnLoopExits");
f("mergedLoopExits")
f("noCompatInnerTrees");
}
function test(f)
{
if (!testName || testName == f.name) {
var expectedJITstats = f.jitstats;
if (expectedJITstats)
{
var expectedProps = {};
jitstatHandler(function(prop) {
if (prop in expectedJITstats)
expectedProps[prop] = true;
});
for (var p in expectedJITstats)
{
if (!(p in expectedProps))
throw "Bad property in " + f.name + ".expected: " + p;
}
}
// Collect our jit stats
var localJITstats = {};
jitstatHandler(function(prop, local, global) {
jitstatHandler(function(prop) {
localJITstats[prop] = tracemonkey[prop];
});
check(f.name, f(), f.expected, localJITstats, f.jitstats);
check(f.name, f(), f.expected, localJITstats, expectedJITstats);
}
}
@ -2157,6 +2174,15 @@ function testNumToString() {
testNumToString.expected = "123456789,-123456789,123456789,-123456789,75bcd15,-75bcd15,21i3v9,-21i3v9";
test(testNumToString);
function testLongNumToString() {
var s;
for (var i = 0; i < 5; i++)
s = (0x08000000).toString(2);
return s;
}
testLongNumToString.expected = '1000000000000000000000000000';
test(testLongNumToString);
function testSubstring() {
for (var i = 0; i < 5; ++i) {
actual = "".substring(5);
@ -3639,12 +3665,12 @@ test(testUnaryImacros);
function testAddAnyInconvertibleObject()
{
var count = 0;
function toString() { ++count; if (count == 5) return {}; return "" + count; }
function toString() { ++count; if (count == 95) return {}; return "" + count; }
var threw = false;
try
{
for (var i = 0; i < 10; i++)
for (var i = 0; i < 100; i++)
{
var o = {valueOf: undefined, toString: toString};
var q = 5 + o;
@ -3653,12 +3679,12 @@ function testAddAnyInconvertibleObject()
catch (e)
{
threw = true;
if (i !== 4)
return "expected i === 4, got " + i;
if (q !== "54")
return "expected q === '54', got " + q + " (type " + typeof q + ")";
if (count !== 5)
return "expected count === 5, got " + count;
if (i !== 94)
return "expected i === 94, got " + i;
if (q !== "594")
return "expected q === '594', got " + q + " (type " + typeof q + ")";
if (count !== 95)
return "expected count === 95, got " + count;
}
if (!threw)
return "expected throw with 5 + o"; // hey, a rhyme!
@ -3669,19 +3695,25 @@ testAddAnyInconvertibleObject.expected = "pass";
testAddAnyInconvertibleObject.jitstats = {
recorderStarted: 1,
recorderAborted: 0,
sideExits: 1
sideExitIntoInterpreter: 93
};
test(testAddAnyInconvertibleObject);
function testAddInconvertibleObjectAny()
{
var count = 0;
function toString() { ++count; if (count == 5) return {}; return "" + count; }
function toString()
{
++count;
if (count == 95)
return {};
return "" + count;
}
var threw = false;
try
{
for (var i = 0; i < 10; i++)
for (var i = 0; i < 100; i++)
{
var o = {valueOf: undefined, toString: toString};
var q = o + 5;
@ -3690,12 +3722,12 @@ function testAddInconvertibleObjectAny()
catch (e)
{
threw = true;
if (i !== 4)
return "expected i === 4, got " + i;
if (q !== "45")
return "expected q === '54', got " + q + " (type " + typeof q + ")";
if (count !== 5)
return "expected count === 5, got " + count;
if (i !== 94)
return "expected i === 94, got " + i;
if (q !== "945")
return "expected q === '945', got " + q + " (type " + typeof q + ")";
if (count !== 95)
return "expected count === 95, got " + count;
}
if (!threw)
return "expected throw with o + 5";
@ -3706,21 +3738,21 @@ testAddInconvertibleObjectAny.expected = "pass";
testAddInconvertibleObjectAny.jitstats = {
recorderStarted: 1,
recorderAborted: 0,
sideExits: 1
sideExitIntoInterpreter: 93
};
test(testAddInconvertibleObjectAny);
function testAddInconvertibleObjectInconvertibleObject()
{
var count1 = 0;
function toString1() { ++count1; if (count1 == 5) return {}; return "" + count1; }
function toString1() { ++count1; if (count1 == 95) return {}; return "" + count1; }
var count2 = 0;
function toString2() { ++count2; if (count2 == 5) return {}; return "" + count2; }
function toString2() { ++count2; if (count2 == 95) return {}; return "" + count2; }
var threw = false;
try
{
for (var i = 0; i < 10; i++)
for (var i = 0; i < 100; i++)
{
var o1 = {valueOf: undefined, toString: toString1};
var o2 = {valueOf: undefined, toString: toString2};
@ -3730,14 +3762,14 @@ function testAddInconvertibleObjectInconvertibleObject()
catch (e)
{
threw = true;
if (i !== 4)
return "expected i === 4, got " + i;
if (q !== "44")
return "expected q === '44', got " + q + " (type " + typeof q + ")";
if (count1 !== 5)
return "expected count1 === 5, got " + count1;
if (count2 !== 4)
return "expected count2 === 5, got " + count2;
if (i !== 94)
return "expected i === 94, got " + i;
if (q !== "9494")
return "expected q === '9494', got " + q + " (type " + typeof q + ")";
if (count1 !== 95)
return "expected count1 === 95, got " + count1;
if (count2 !== 94)
return "expected count2 === 94, got " + count2;
}
if (!threw)
return "expected throw with o1 + o2";
@ -3748,33 +3780,33 @@ testAddInconvertibleObjectInconvertibleObject.expected = "pass";
testAddInconvertibleObjectInconvertibleObject.jitstats = {
recorderStarted: 1,
recorderAborted: 0,
sideExits: 1
sideExitIntoInterpreter: 93
};
test(testAddInconvertibleObjectInconvertibleObject);
function testBitOrAnyInconvertibleObject()
{
var count = 0;
function toString() { ++count; if (count == 5) return {}; return count; }
function toString() { ++count; if (count == 95) return {}; return count; }
var threw = false;
try
{
for (var i = 0; i < 10; i++)
for (var i = 0; i < 100; i++)
{
var o = {valueOf: undefined, toString: toString};
var q = 2 | o;
var q = 1 | o;
}
}
catch (e)
{
threw = true;
if (i !== 4)
return "expected i === 4, got " + i;
if (q !== 6)
return "expected q === 6, got " + q;
if (count !== 5)
return "expected count === 5, got " + count;
if (i !== 94)
return "expected i === 94, got " + i;
if (q !== 95)
return "expected q === 95, got " + q;
if (count !== 95)
return "expected count === 95, got " + count;
}
if (!threw)
return "expected throw with 2 | o"; // hey, a rhyme!
@ -3785,33 +3817,33 @@ testBitOrAnyInconvertibleObject.expected = "pass";
testBitOrAnyInconvertibleObject.jitstats = {
recorderStarted: 1,
recorderAborted: 0,
sideExits: 1
sideExitIntoInterpreter: 93
};
test(testBitOrAnyInconvertibleObject);
function testBitOrInconvertibleObjectAny()
{
var count = 0;
function toString() { ++count; if (count == 5) return {}; return count; }
function toString() { ++count; if (count == 95) return {}; return count; }
var threw = false;
try
{
for (var i = 0; i < 10; i++)
for (var i = 0; i < 100; i++)
{
var o = {valueOf: undefined, toString: toString};
var q = o | 2;
var q = o | 1;
}
}
catch (e)
{
threw = true;
if (i !== 4)
if (i !== 94)
return "expected i === 4, got " + i;
if (q !== 6)
return "expected q === 6, got " + q;
if (count !== 5)
return "expected count === 5, got " + count;
if (q !== 95)
return "expected q === 95, got " + q;
if (count !== 95)
return "expected count === 95, got " + count;
}
if (!threw)
return "expected throw with o | 2";
@ -3822,21 +3854,21 @@ testBitOrInconvertibleObjectAny.expected = "pass";
testBitOrInconvertibleObjectAny.jitstats = {
recorderStarted: 1,
recorderAborted: 0,
sideExits: 1
sideExitIntoInterpreter: 93
};
test(testBitOrInconvertibleObjectAny);
function testBitOrInconvertibleObjectInconvertibleObject()
{
var count1 = 0;
function toString1() { ++count1; if (count1 == 5) return {}; return count1; }
function toString1() { ++count1; if (count1 == 95) return {}; return count1; }
var count2 = 0;
function toString2() { ++count2; if (count2 == 5) return {}; return count2; }
function toString2() { ++count2; if (count2 == 95) return {}; return count2; }
var threw = false;
try
{
for (var i = 0; i < 10; i++)
for (var i = 0; i < 100; i++)
{
var o1 = {valueOf: undefined, toString: toString1};
var o2 = {valueOf: undefined, toString: toString2};
@ -3846,14 +3878,14 @@ function testBitOrInconvertibleObjectInconvertibleObject()
catch (e)
{
threw = true;
if (i !== 4)
return "expected i === 4, got " + i;
if (q !== 4)
return "expected q === 4, got " + q;
if (count1 !== 5)
return "expected count1 === 5, got " + count1;
if (count2 !== 4)
return "expected count2 === 5, got " + count2;
if (i !== 94)
return "expected i === 94, got " + i;
if (q !== 94)
return "expected q === 94, got " + q;
if (count1 !== 95)
return "expected count1 === 95, got " + count1;
if (count2 !== 94)
return "expected count2 === 94, got " + count2;
}
if (!threw)
return "expected throw with o1 | o2";
@ -3864,7 +3896,7 @@ testBitOrInconvertibleObjectInconvertibleObject.expected = "pass";
testBitOrInconvertibleObjectInconvertibleObject.jitstats = {
recorderStarted: 1,
recorderAborted: 0,
sideExits: 1
sideExitIntoInterpreter: 93
};
test(testBitOrInconvertibleObjectInconvertibleObject);
@ -3946,6 +3978,17 @@ function testLirBufOOM()
testLirBufOOM.expected = "ok";
test(testLirBufOOM);
function testStringResolve() {
var x = 0;
for each (let d in [new String('q'), new String('q'), new String('q')]) {
if (("" + (0 in d)) === "true")
x++;
}
return x;
}
testStringResolve.expected = 3;
test(testStringResolve);
/*****************************************************************************
* *
* _____ _ _ _____ ______ _____ _______ *

View File

@ -3404,12 +3404,8 @@ ContextHolder::ContextHolder(JSContext *aOuterCx, JSObject *aSandbox)
JSOPTION_PRIVATE_IS_NSISUPPORTS);
JS_SetGlobalObject(mJSContext, aSandbox);
JS_SetContextPrivate(mJSContext, this);
if(JS_GetOperationCallback(aOuterCx))
{
JS_SetOperationCallback(mJSContext, ContextHolderOperationCallback,
JS_GetOperationLimit(aOuterCx));
}
JS_SetOperationCallback(mJSContext, ContextHolderOperationCallback,
JS_GetOperationLimit(aOuterCx));
}
}
@ -3424,19 +3420,8 @@ ContextHolder::ContextHolderOperationCallback(JSContext *cx)
JSOperationCallback callback = JS_GetOperationCallback(origCx);
JSBool ok = JS_TRUE;
if(callback)
{
ok = callback(origCx);
callback = JS_GetOperationCallback(origCx);
if(callback)
{
// If the callback is still set in the original context, reflect
// a possibly updated operation limit into cx.
JS_SetOperationLimit(cx, JS_GetOperationLimit(origCx));
return ok;
}
}
JS_ClearOperationCallback(cx);
JS_SetOperationLimit(cx, JS_GetOperationLimit(origCx));
return ok;
}

View File

@ -126,6 +126,12 @@ else
TEST_DRIVER_PPARGS += -DIS_TEST_BUILD=0
endif
ifeq ($(MOZ_DEBUG), 1)
TEST_DRIVER_PPARGS += -DIS_DEBUG_BUILD=1
else
TEST_DRIVER_PPARGS += -DIS_DEBUG_BUILD=0
endif
runtests.py: runtests.py.in
$(PYTHON) $(topsrcdir)/config/Preprocessor.py \
$(TEST_DRIVER_PPARGS) $(DEFINES) $(ACDEFINES) $^ > $@