mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-02 01:48:05 +00:00
Merge tm to m-c.
This commit is contained in:
commit
6d6645ee0b
@ -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 = \
|
||||
|
@ -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) $^ > $@
|
||||
|
@ -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.
|
||||
|
@ -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) \
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.)
|
||||
|
@ -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) {
|
||||
|
@ -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) \
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
{
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
/*****************************************************************************
|
||||
* *
|
||||
* _____ _ _ _____ ______ _____ _______ *
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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) $^ > $@
|
||||
|
Loading…
Reference in New Issue
Block a user