Add imacros to support conversion of arbitrary JSObjects (456511, r=gal).

This commit is contained in:
Brendan 2008-11-12 16:55:45 -08:00
parent 31d283c656
commit 0ca607d76b
21 changed files with 827 additions and 215 deletions

View File

@ -1547,6 +1547,16 @@ Array_p_join(JSContext* cx, JSObject* obj, JSString *str)
JS_ASSERT(JSVAL_IS_STRING(v));
return JSVAL_TO_STRING(v);
}
static JSString* FASTCALL
Array_p_toString(JSContext* cx, JSObject* obj)
{
jsval v;
if (!array_join_sub(cx, obj, TO_STRING, NULL, &v))
return NULL;
JS_ASSERT(JSVAL_IS_STRING(v));
return JSVAL_TO_STRING(v);
}
#endif
/*
@ -2967,18 +2977,20 @@ static JSPropertySpec array_props[] = {
{0,0,0,0,0}
};
JS_DEFINE_TRCINFO_1(array_toString,
(2, (static, STRING_FAIL, Array_p_toString, CONTEXT, THIS, 0, 0)))
JS_DEFINE_TRCINFO_1(array_join,
(3, (static, STRING_FAIL, Array_p_join, CONTEXT, THIS, STRING, 0, 0)))
JS_DEFINE_TRCINFO_1(array_push,
(3, (static, JSVAL_FAIL, Array_p_push1, CONTEXT, THIS, JSVAL, 0, 0)))
(3, (static, JSVAL_FAIL, Array_p_push1, CONTEXT, THIS, JSVAL, 0, 0)))
JS_DEFINE_TRCINFO_1(array_pop,
(2, (static, JSVAL_FAIL, Array_p_pop, CONTEXT, THIS, 0, 0)))
(2, (static, JSVAL_FAIL, Array_p_pop, CONTEXT, THIS, 0, 0)))
static JSFunctionSpec array_methods[] = {
#if JS_HAS_TOSOURCE
JS_FN(js_toSource_str, array_toSource, 0,0),
#endif
JS_FN(js_toString_str, array_toString, 0,0),
JS_TN(js_toString_str, array_toString, 0,0, array_toString_trcinfo),
JS_FN(js_toLocaleString_str,array_toLocaleString,0,0),
/* Perl-ish methods. */

View File

@ -156,9 +156,18 @@ const char *const js_common_atom_names[] = {
js_current_str, /* currentAtom */
#endif
};
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names) * sizeof(JSAtom *) ==
LAZY_ATOM_OFFSET_START - ATOM_OFFSET_START);
/*
* Interpreter macros called by the trace recorder assume common atom indexes
* fit in one byte of immediate operand.
*/
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names) < 256);
const size_t js_common_atom_count = JS_ARRAY_LENGTH(js_common_atom_names);
const char js_anonymous_str[] = "anonymous";
const char js_apply_str[] = "apply";
const char js_arguments_str[] = "arguments";

View File

@ -266,7 +266,13 @@ struct JSAtomState {
#define ATOM_OFFSET_LIMIT (sizeof(JSAtomState))
#define COMMON_ATOMS_START(state) \
(JSAtom **)((uint8 *)(state) + ATOM_OFFSET_START)
((JSAtom **)((uint8 *)(state) + ATOM_OFFSET_START))
#define COMMON_ATOM_INDEX(name) \
((offsetof(JSAtomState, name##Atom) - ATOM_OFFSET_START) \
/ sizeof(JSAtom*))
#define COMMON_TYPE_ATOM_INDEX(type) \
((offsetof(JSAtomState, typeAtoms[type]) - ATOM_OFFSET_START) \
/ sizeof(JSAtom*))
/* Start and limit offsets should correspond to atoms. */
JS_STATIC_ASSERT(ATOM_OFFSET_START % sizeof(JSAtom *) == 0);
@ -280,6 +286,7 @@ JS_STATIC_ASSERT(ATOM_OFFSET_LIMIT % sizeof(JSAtom *) == 0);
((cx)->runtime->atomState.classAtoms[JSProto_##name])
extern const char *const js_common_atom_names[];
extern const size_t js_common_atom_count;
/*
* Macros to access C strings for JSType and boolean literals together with

View File

@ -920,7 +920,7 @@ js_ReportOutOfMemory(JSContext *cx)
for (fp = cx->fp; fp; fp = fp->down) {
if (fp->regs) {
report.filename = fp->script->filename;
report.lineno = js_PCToLineNumber(cx, fp->script, fp->regs->pc);
report.lineno = js_FramePCToLineNumber(cx, fp);
break;
}
}
@ -990,7 +990,7 @@ js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap)
for (fp = cx->fp; fp; fp = fp->down) {
if (fp->regs) {
report.filename = fp->script->filename;
report.lineno = js_PCToLineNumber(cx, fp->script, fp->regs->pc);
report.lineno = js_FramePCToLineNumber(cx, fp);
break;
}
}
@ -1202,7 +1202,7 @@ js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback,
for (fp = cx->fp; fp; fp = fp->down) {
if (fp->regs) {
report.filename = fp->script->filename;
report.lineno = js_PCToLineNumber(cx, fp->script, fp->regs->pc);
report.lineno = js_FramePCToLineNumber(cx, fp);
break;
}
}

View File

@ -1968,10 +1968,30 @@ date_toString(JSContext *cx, uintN argc, jsval *vp)
return date_format(cx, utctime, FORMATSPEC_FULL, vp);
}
#ifdef JS_TRACER
static jsval FASTCALL
date_valueOf_tn(JSContext* cx, JSObject* obj, JSString* str)
{
JS_ASSERT(JS_InstanceOf(cx, obj, &js_DateClass, NULL));
jsdouble t = *JSVAL_TO_DOUBLE(obj->fslots[JSSLOT_UTC_TIME]);
JSString* number_str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
jsval v;
if (js_EqualStrings(str, number_str)) {
if (!js_NewNumberInRootedValue(cx, t, &v))
return JSVAL_ERROR_COOKIE;
} else {
if (!date_format(cx, t, FORMATSPEC_FULL, &v))
return JSVAL_ERROR_COOKIE;
}
return v;
}
#endif
static JSBool
date_valueOf(JSContext *cx, uintN argc, jsval *vp)
{
JSString *str, *str2;
JSString *str, *number_str;
/* It is an error to call date_valueOf on a non-date object, but we don't
* need to check for that explicitly here because every path calls
@ -1986,8 +2006,8 @@ date_valueOf(JSContext *cx, uintN argc, jsval *vp)
str = js_ValueToString(cx, vp[2]);
if (!str)
return JS_FALSE;
str2 = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
if (js_EqualStrings(str, str2))
number_str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
if (js_EqualStrings(str, number_str))
return date_getTime(cx, argc, vp);
return date_toString(cx, argc, vp);
}
@ -2005,6 +2025,9 @@ static JSFunctionSpec date_static_methods[] = {
JS_FS_END
};
JS_DEFINE_TRCINFO_1(date_valueOf,
(3, (static, JSVAL_FAIL, date_valueOf_tn, CONTEXT, THIS, STRING, 0, 0)))
static JSFunctionSpec date_methods[] = {
JS_FN("getTime", date_getTime, 0,0),
JS_FN("getTimezoneOffset", date_getTimezoneOffset, 0,0),
@ -2055,7 +2078,7 @@ static JSFunctionSpec date_methods[] = {
JS_FN(js_toSource_str, date_toSource, 0,0),
#endif
JS_FN(js_toString_str, date_toString, 0,0),
JS_FN(js_valueOf_str, date_valueOf, 0,0),
JS_TN(js_valueOf_str, date_valueOf, 0,0, date_valueOf_trcinfo),
JS_FS_END
};

View File

@ -338,7 +338,7 @@ InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message,
if (fp->script) {
elem->filename = fp->script->filename;
if (fp->regs)
elem->ulineno = js_PCToLineNumber(cx, fp->script, fp->regs->pc);
elem->ulineno = js_FramePCToLineNumber(cx, fp);
}
++elem;
}
@ -804,9 +804,7 @@ Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
} else {
if (!fp)
fp = JS_GetScriptedCaller(cx, NULL);
lineno = (fp && fp->regs)
? js_PCToLineNumber(cx, fp->script, fp->regs->pc)
: 0;
lineno = (fp && fp->regs) ? js_FramePCToLineNumber(cx, fp) : 0;
}
return (OBJ_GET_CLASS(cx, obj) != &js_ErrorClass) ||

View File

@ -309,7 +309,8 @@ js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
JSPropCacheEntry *entry;
uint32 vcap;
JS_ASSERT(JS_UPTRDIFF(pc, cx->fp->script->code) < cx->fp->script->length);
JS_ASSERT(uintN((cx->fp->imacpc ? cx->fp->imacpc : pc) - cx->fp->script->code)
< cx->fp->script->length);
op = (JSOp) *pc;
cs = &js_CodeSpec[op];
@ -1260,6 +1261,7 @@ have_fun:
frame.annotation = NULL;
frame.scopeChain = NULL; /* set below for real, after cx->fp is set */
frame.regs = NULL;
frame.imacpc = NULL;
frame.slots = NULL;
frame.sharpDepth = 0;
frame.sharpArray = NULL;
@ -1492,6 +1494,8 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
frame.annotation = NULL;
frame.sharpArray = NULL;
}
frame.imacpc = NULL;
if (script->nslots != 0) {
frame.slots = js_AllocRawStack(cx, script->nslots, &mark);
if (!frame.slots) {
@ -2026,7 +2030,8 @@ js_TraceOpcode(JSContext *cx, jsint len)
fputc('\n', tracefp);
}
fprintf(tracefp, "%4u: ", js_PCToLineNumber(cx, fp->script, regs->pc));
fprintf(tracefp, "%4u: ",
js_PCToLineNumber(cx, fp->script, fp->imacpc ? fp->imacpc : regs->pc));
js_Disassemble1(cx, fp->script, regs->pc,
PTRDIFF(regs->pc, fp->script->code, jsbytecode),
JS_FALSE, tracefp);
@ -2601,9 +2606,12 @@ js_Interpret(JSContext *cx)
#define LOAD_ATOM(PCOFF) \
JS_BEGIN_MACRO \
JS_ASSERT((size_t)(atoms - script->atomMap.vector) < \
(size_t)(script->atomMap.length - \
GET_INDEX(regs.pc + PCOFF))); \
JS_ASSERT(fp->imacpc \
? atoms == COMMON_ATOMS_START(&rt->atomState) && \
GET_INDEX(regs.pc + PCOFF) < js_common_atom_count \
: (size_t)(atoms - script->atomMap.vector) < \
(size_t)(script->atomMap.length - \
GET_INDEX(regs.pc + PCOFF))); \
atom = atoms[GET_INDEX(regs.pc + PCOFF)]; \
JS_END_MACRO
@ -2939,6 +2947,23 @@ js_Interpret(JSContext *cx)
* will be false after the inline_return label.
*/
ASSERT_NOT_THROWING(cx);
if (fp->imacpc) {
/*
* If we are at the end of an imacro, return to its caller in
* the current frame.
*/
JS_ASSERT(op == JSOP_STOP);
end_imacro:
JS_ASSERT((uintN)(regs.sp - fp->slots) < script->nslots);
regs.pc = fp->imacpc + js_CodeSpec[*fp->imacpc].length;
fp->imacpc = NULL;
atoms = script->atomMap.vector;
op = JSOp(*regs.pc);
DO_OP();
}
JS_ASSERT(regs.sp == StackBase(fp));
if ((fp->flags & JSFRAME_CONSTRUCTING) &&
JSVAL_IS_PRIMITIVE(fp->rval)) {
@ -3039,10 +3064,7 @@ js_Interpret(JSContext *cx)
/* Resume execution in the calling frame. */
inlineCallCount--;
if (JS_LIKELY(ok)) {
#ifdef JS_TRACER
if (TRACE_RECORDER(cx))
RECORD(LeaveFrame);
#endif
TRACE_0(LeaveFrame);
JS_ASSERT(js_CodeSpec[*regs.pc].length == JSOP_CALL_LENGTH);
len = JSOP_CALL_LENGTH;
DO_NEXT_OP(len);
@ -3286,6 +3308,14 @@ js_Interpret(JSContext *cx)
PUSH(rval);
END_CASE(JSOP_DUP2)
BEGIN_CASE(JSOP_SWAP)
JS_ASSERT(regs.sp - 2 >= StackBase(fp));
lval = FETCH_OPND(-2);
rval = FETCH_OPND(-1);
STORE_OPND(-1, lval);
STORE_OPND(-2, rval);
END_CASE(JSOP_SWAP)
#define PROPERTY_OP(n, call) \
JS_BEGIN_MACRO \
/* Fetch the left part and resolve it to a non-null object. */ \
@ -3471,6 +3501,11 @@ js_Interpret(JSContext *cx)
PUSH_OPND(OBJECT_TO_JSVAL(obj));
END_CASE(JSOP_BINDNAME)
BEGIN_CASE(JSOP_IMACOP)
JS_ASSERT(JS_UPTRDIFF(fp->imacpc, script->code) < script->length);
op = JSOp(*fp->imacpc);
DO_OP();
#define BITWISE_OP(OP) \
JS_BEGIN_MACRO \
FETCH_INT(cx, -2, i); \
@ -4981,6 +5016,7 @@ js_Interpret(JSContext *cx)
newifp->frame.thisp = (JSObject *)vp[1];
newifp->frame.regs = NULL;
newifp->frame.imacpc = NULL;
newifp->frame.slots = newsp;
/* Push void to initialize local variables. */
@ -5020,10 +5056,7 @@ js_Interpret(JSContext *cx)
newifp->frame.regs = &regs;
cx->fp = fp = &newifp->frame;
#ifdef JS_TRACER
if (TRACE_RECORDER(cx))
RECORD(EnterFrame);
#endif
TRACE_0(EnterFrame);
inlineCallCount++;
JS_RUNTIME_METER(rt, inlineCalls);
@ -6883,8 +6916,6 @@ js_Interpret(JSContext *cx)
L_JSOP_DEFXMLNS:
# endif
L_JSOP_UNUSED79:
L_JSOP_UNUSED103:
L_JSOP_UNUSED131:
L_JSOP_UNUSED201:
L_JSOP_UNUSED202:
@ -6930,6 +6961,25 @@ js_Interpret(JSContext *cx)
#endif /* !JS_THREADED_INTERP */
error:
if (fp->imacpc && cx->throwing) {
// To keep things simple, we hard-code imacro exception handlers here.
if (*fp->imacpc == JSOP_NEXTITER) {
JS_ASSERT(*regs.pc == JSOP_CALL);
if (js_ValueIsStopIteration(cx->exception)) {
cx->throwing = JS_FALSE;
cx->exception = JSVAL_VOID;
regs.sp[-1] = JSVAL_HOLE;
PUSH(JSVAL_FALSE);
goto end_imacro;
}
}
// Handle other exceptions as if they came from the imacro-calling pc.
regs.pc = fp->imacpc;
fp->imacpc = NULL;
atoms = script->atomMap.vector;
}
JS_ASSERT((size_t)(regs.pc - script->code) < script->length);
if (!cx->throwing) {
/* This is an error, not a catchable exception, quit the frame ASAP. */

View File

@ -68,6 +68,7 @@ typedef struct JSFrameRegs {
*/
struct JSStackFrame {
JSFrameRegs *regs;
jsbytecode *imacpc; /* null or interpreter macro call pc */
jsval *slots; /* variables, locals and operand stack */
JSObject *callobj; /* lazily created Call object */
JSObject *argsobj; /* lazily created arguments object */
@ -95,6 +96,16 @@ struct JSStackFrame {
#endif
};
#ifdef DEBUG
#ifdef __cplusplus
static JS_INLINE uintN
FramePCOffset(JSStackFrame* fp)
{
return uintN((fp->imacpc ? fp->imacpc : fp->regs->pc) - fp->script->code);
}
#endif
#endif
static JS_INLINE jsval *
StackBase(JSStackFrame *fp)
{

View File

@ -612,12 +612,8 @@ js_CallIteratorNext(JSContext *cx, JSObject *iterobj, jsval *rval)
return JS_FALSE;
if (!js_InternalCall(cx, iterobj, *rval, 0, NULL, rval)) {
/* Check for StopIteration. */
if (!cx->throwing ||
JSVAL_IS_PRIMITIVE(cx->exception) ||
OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(cx->exception))
!= &js_StopIterationClass) {
if (!cx->throwing || !js_ValueIsStopIteration(cx->exception))
return JS_FALSE;
}
/* Inline JS_ClearPendingException(cx). */
cx->throwing = JS_FALSE;
@ -633,8 +629,7 @@ js_CallIteratorNext(JSContext *cx, JSObject *iterobj, jsval *rval)
static JSBool
stopiter_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
{
*bp = !JSVAL_IS_PRIMITIVE(v) &&
OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_StopIterationClass;
*bp = js_ValueIsStopIteration(v);
return JS_TRUE;
}
@ -717,7 +712,7 @@ JSObject *
js_NewGenerator(JSContext *cx, JSStackFrame *fp)
{
JSObject *obj;
uintN argc, nargs, nvars, nslots;
uintN argc, nargs, nslots;
JSGenerator *gen;
jsval *slots;
@ -729,7 +724,6 @@ js_NewGenerator(JSContext *cx, JSStackFrame *fp)
/* Load and compute stack slot counts. */
argc = fp->argc;
nargs = JS_MAX(argc, fp->fun->nargs);
nvars = fp->fun->u.i.nvars;
nslots = 2 + nargs + fp->script->nslots;
/* Allocate obj's private data struct. */
@ -780,6 +774,7 @@ js_NewGenerator(JSContext *cx, JSStackFrame *fp)
gen->frame.annotation = NULL;
gen->frame.scopeChain = fp->scopeChain;
gen->frame.imacpc = NULL;
gen->frame.slots = slots;
JS_ASSERT(StackBase(fp) == fp->regs->sp);
gen->savedRegs.sp = slots + fp->script->nfixed;

View File

@ -125,6 +125,13 @@ extern JSClass js_GeneratorClass;
extern JSClass js_IteratorClass;
extern JSClass js_StopIterationClass;
static inline bool
js_ValueIsStopIteration(jsval v)
{
return !JSVAL_IS_PRIMITIVE(v) &&
STOBJ_GET_CLASS(JSVAL_TO_OBJECT(v)) == &js_StopIterationClass;
}
extern JSObject *
js_InitIteratorClasses(JSContext *cx, JSObject *obj);

View File

@ -604,6 +604,12 @@ MATH_BUILTIN_1(sqrt)
MATH_BUILTIN_1(floor)
MATH_BUILTIN_1(ceil)
static jsdouble FASTCALL
math_abs_tn(jsdouble d)
{
return fabs(d);
}
static jsdouble FASTCALL
math_log_tn(jsdouble d)
{
@ -648,6 +654,14 @@ math_random_tn(JSRuntime* rt)
return z;
}
static jsdouble FASTCALL
math_round_tn(jsdouble x)
{
return js_copysign(floor(x + 0.5), x);
}
JS_DEFINE_TRCINFO_1(math_abs,
(1, (static, DOUBLE, math_abs_tn, DOUBLE, 1, 1)))
JS_DEFINE_TRCINFO_1(math_log,
(1, (static, DOUBLE, math_log_tn, DOUBLE, 1, 1)))
JS_DEFINE_TRCINFO_1(math_max,
@ -656,6 +670,8 @@ JS_DEFINE_TRCINFO_1(math_pow,
(2, (static, DOUBLE, math_pow_tn, DOUBLE, DOUBLE, 1, 1)))
JS_DEFINE_TRCINFO_1(math_random,
(1, (static, DOUBLE, math_random_tn, RUNTIME, 0, 0)))
JS_DEFINE_TRCINFO_1(math_round,
(1, (static, DOUBLE, math_round_tn, DOUBLE, 1, 1)))
#endif /* JS_TRACER */
@ -663,7 +679,7 @@ static JSFunctionSpec math_static_methods[] = {
#if JS_HAS_TOSOURCE
JS_FN(js_toSource_str, math_toSource, 0, 0),
#endif
JS_FN("abs", math_abs, 1, 0),
JS_TN("abs", math_abs, 1, 0, math_abs_trcinfo),
JS_FN("acos", math_acos, 1, 0),
JS_FN("asin", math_asin, 1, 0),
JS_FN("atan", math_atan, 1, 0),
@ -677,7 +693,7 @@ static JSFunctionSpec math_static_methods[] = {
JS_FN("min", math_min, 2, 0),
JS_TN("pow", math_pow, 2, 0, math_pow_trcinfo),
JS_TN("random", math_random, 0, 0, math_random_trcinfo),
JS_FN("round", math_round, 1, 0),
JS_TN("round", math_round, 1, 0, math_round_trcinfo),
JS_TN("sin", math_sin, 1, 0, math_sin_trcinfo),
JS_TN("sqrt", math_sqrt, 1, 0, math_sqrt_trcinfo),
JS_FN("tan", math_tan, 1, 0),

View File

@ -1171,8 +1171,7 @@ js_ComputeFilename(JSContext *cx, JSStackFrame *caller,
JS_ASSERT(caller->regs->pc[JSOP_EVAL_LENGTH] == JSOP_LINENO);
*linenop = GET_UINT16(caller->regs->pc + JSOP_EVAL_LENGTH);
} else {
*linenop = js_PCToLineNumber(cx, caller->script,
caller->regs ? caller->regs->pc : NULL);
*linenop = js_FramePCToLineNumber(cx, caller);
}
return caller->script->filename;
}

View File

@ -127,23 +127,23 @@ OPDEF(JSOP_FORLOCAL, 11, "forlocal", NULL, 3, 2, 2, 19, JOF_LOCAL|
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)
OPDEF(JSOP_BITOR, 15, "bitor", "|", 1, 2, 1, 7, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_BITXOR, 16, "bitxor", "^", 1, 2, 1, 8, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_BITAND, 17, "bitand", "&", 1, 2, 1, 9, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_BITOR, 15, "bitor", "|", 1, 2, 1, 7, JOF_BYTE|JOF_LEFTASSOC|JOF_TMPSLOT2)
OPDEF(JSOP_BITXOR, 16, "bitxor", "^", 1, 2, 1, 8, JOF_BYTE|JOF_LEFTASSOC|JOF_TMPSLOT2)
OPDEF(JSOP_BITAND, 17, "bitand", "&", 1, 2, 1, 9, JOF_BYTE|JOF_LEFTASSOC|JOF_TMPSLOT2)
OPDEF(JSOP_EQ, 18, "eq", "==", 1, 2, 1, 10, JOF_BYTE|JOF_LEFTASSOC|JOF_DETECTING)
OPDEF(JSOP_NE, 19, "ne", "!=", 1, 2, 1, 10, JOF_BYTE|JOF_LEFTASSOC|JOF_DETECTING)
OPDEF(JSOP_LT, 20, "lt", "<", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_LE, 21, "le", "<=", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_GT, 22, "gt", ">", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_GE, 23, "ge", ">=", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_LSH, 24, "lsh", "<<", 1, 2, 1, 12, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_RSH, 25, "rsh", ">>", 1, 2, 1, 12, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_URSH, 26, "ursh", ">>>", 1, 2, 1, 12, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_ADD, 27, "add", "+", 1, 2, 1, 13, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_SUB, 28, "sub", "-", 1, 2, 1, 13, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_MUL, 29, "mul", "*", 1, 2, 1, 14, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_DIV, 30, "div", "/", 1, 2, 1, 14, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_MOD, 31, "mod", "%", 1, 2, 1, 14, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_LSH, 24, "lsh", "<<", 1, 2, 1, 12, JOF_BYTE|JOF_LEFTASSOC|JOF_TMPSLOT2)
OPDEF(JSOP_RSH, 25, "rsh", ">>", 1, 2, 1, 12, JOF_BYTE|JOF_LEFTASSOC|JOF_TMPSLOT2)
OPDEF(JSOP_URSH, 26, "ursh", ">>>", 1, 2, 1, 12, JOF_BYTE|JOF_LEFTASSOC|JOF_TMPSLOT2)
OPDEF(JSOP_ADD, 27, "add", "+", 1, 2, 1, 13, JOF_BYTE|JOF_LEFTASSOC|JOF_TMPSLOT2)
OPDEF(JSOP_SUB, 28, "sub", "-", 1, 2, 1, 13, JOF_BYTE|JOF_LEFTASSOC|JOF_TMPSLOT2)
OPDEF(JSOP_MUL, 29, "mul", "*", 1, 2, 1, 14, JOF_BYTE|JOF_LEFTASSOC|JOF_TMPSLOT2)
OPDEF(JSOP_DIV, 30, "div", "/", 1, 2, 1, 14, JOF_BYTE|JOF_LEFTASSOC|JOF_TMPSLOT2)
OPDEF(JSOP_MOD, 31, "mod", "%", 1, 2, 1, 14, JOF_BYTE|JOF_LEFTASSOC|JOF_TMPSLOT2)
OPDEF(JSOP_NOT, 32, "not", "!", 1, 1, 1, 15, JOF_BYTE|JOF_DETECTING)
OPDEF(JSOP_BITNOT, 33, "bitnot", "~", 1, 1, 1, 15, JOF_BYTE)
OPDEF(JSOP_NEG, 34, "neg", "- ", 1, 1, 1, 15, JOF_BYTE)
@ -213,7 +213,7 @@ OPDEF(JSOP_NEXTITER, 76, "nextiter", NULL, 1, 2, 3, 0, JOF_BYTE)
OPDEF(JSOP_ENDITER, 77, "enditer", NULL, 1, 2, 0, 0, JOF_BYTE)
OPDEF(JSOP_APPLY, 78, "apply", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE)
OPDEF(JSOP_UNUSED79, 79, "unused79", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_SWAP, 79, "swap", NULL, 1, 2, 2, 0, JOF_BYTE)
/* Push object literal. */
OPDEF(JSOP_OBJECT, 80, "object", NULL, 3, 0, 1, 19, JOF_OBJECT)
@ -255,7 +255,7 @@ OPDEF(JSOP_DECLOCAL, 100,"declocal", NULL, 3, 0, 1, 15, JOF_LOCAL|
OPDEF(JSOP_LOCALINC, 101,"localinc", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_INC|JOF_POST)
OPDEF(JSOP_LOCALDEC, 102,"localdec", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_DEC|JOF_POST)
OPDEF(JSOP_UNUSED103, 103,"unused103", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_IMACOP, 103,"imacop", NULL, 1, 0, 0, 0, JOF_BYTE)
/* ECMA-compliant for/in ops. */
OPDEF(JSOP_FORNAME, 104,"forname", NULL, 3, 2, 2, 19, JOF_ATOM|JOF_NAME|JOF_FOR)

View File

@ -3660,8 +3660,8 @@ MatchRegExp(REGlobalData *gData, REMatchState *x)
debug_only_v(printf("entering REGEXP trace at %s:%u@%u, code: %p\n",
gData->cx->fp->script->filename,
js_PCToLineNumber(gData->cx, gData->cx->fp->script, gData->cx->fp->regs->pc),
gData->cx->fp->regs->pc - gData->cx->fp->script->code,
js_FramePCToLineNumber(gData->cx, gData->cx->fp),
FramePCOffset(gData->cx->fp),
fragment->code()););
#if defined(JS_NO_FASTCALL) && defined(NANOJIT_IA32)

View File

@ -1742,6 +1742,12 @@ js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc)
return result;
}
uintN
js_FramePCToLineNumber(JSContext *cx, JSStackFrame *fp)
{
return js_PCToLineNumber(cx, fp->script, fp->imacpc ? fp->imacpc : fp->regs->pc);
}
uintN
js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
{

View File

@ -159,15 +159,20 @@ StackDepth(JSScript *script)
#define JS_GET_SCRIPT_ATOM(script, index, atom) \
JS_BEGIN_MACRO \
JSAtomMap *atoms_ = &(script)->atomMap; \
JS_ASSERT((uint32)(index) < atoms_->length); \
(atom) = atoms_->vector[(index)]; \
if (cx->fp && cx->fp->imacpc) { \
JS_ASSERT((size_t)(index) < js_common_atom_count); \
(atom) = COMMON_ATOMS_START(&cx->runtime->atomState)[index]; \
} else { \
JS_ASSERT((uint32)(index) < atoms_->length); \
(atom) = atoms_->vector[index]; \
} \
JS_END_MACRO
#define JS_GET_SCRIPT_OBJECT(script, index, obj) \
JS_BEGIN_MACRO \
JSObjectArray *objects_ = JS_SCRIPT_OBJECTS(script); \
JS_ASSERT((uint32)(index) < objects_->length); \
(obj) = objects_->vector[(index)]; \
(obj) = objects_->vector[index]; \
JS_END_MACRO
#define JS_GET_SCRIPT_FUNCTION(script, index, fun) \
@ -185,7 +190,7 @@ StackDepth(JSScript *script)
JS_BEGIN_MACRO \
JSObjectArray *regexps_ = JS_SCRIPT_REGEXPS(script); \
JS_ASSERT((uint32)(index) < regexps_->length); \
(obj) = regexps_->vector[(index)]; \
(obj) = regexps_->vector[index]; \
JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_RegExpClass); \
JS_END_MACRO
@ -291,7 +296,14 @@ js_TraceScript(JSTracer *trc, JSScript *script);
extern jssrcnote *
js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc);
/* XXX need cx to lock function objects declared by prolog bytecodes. */
/*
* NOTE: use js_FramePCToLineNumber(cx, fp) when you have an active fp, in
* preference to js_PCToLineNumber (cx, fp->script fp->regs->pc), because
* fp->imacpc may be non-null, indicating an active imacro.
*/
extern uintN
js_FramePCToLineNumber(JSContext *cx, JSStackFrame *fp);
extern uintN
js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc);

View File

@ -788,6 +788,16 @@ str_substring(JSContext *cx, uintN argc, jsval *vp)
}
#ifdef JS_TRACER
static JSString* FASTCALL
String_p_toString(JSContext* cx, JSObject* obj)
{
if (!JS_InstanceOf(cx, obj, &js_StringClass, NULL))
return NULL;
jsval v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
JS_ASSERT(JSVAL_IS_STRING(v));
return JSVAL_TO_STRING(v);
}
static JSString* FASTCALL
String_p_substring(JSContext* cx, JSString* str, int32 begin, int32 end)
{
@ -2471,6 +2481,8 @@ js_String_getelem(JSContext* cx, JSString* str, int32 i)
JS_DEFINE_CALLINFO_2(extern, BOOL, js_EqualStrings, STRING, STRING, 1, 1)
JS_DEFINE_CALLINFO_2(extern, INT32, js_CompareStrings, STRING, STRING, 1, 1)
JS_DEFINE_TRCINFO_1(str_toString,
(2, (extern, STRING_FAIL, String_p_toString, CONTEXT, THIS, 1, 1)))
JS_DEFINE_TRCINFO_2(str_substring,
(4, (static, STRING_FAIL, String_p_substring, CONTEXT, THIS_STRING, INT32, INT32, 1, 1)),
(3, (static, STRING_FAIL, String_p_substring_1, CONTEXT, THIS_STRING, INT32, 1, 1)))
@ -2508,7 +2520,7 @@ static JSFunctionSpec string_methods[] = {
#endif
/* Java-like methods. */
JS_FN(js_toString_str, str_toString, 0,JSFUN_THISP_STRING),
JS_TN(js_toString_str, str_toString, 0,JSFUN_THISP_STRING, str_toString_trcinfo),
JS_FN(js_valueOf_str, str_toString, 0,JSFUN_THISP_STRING),
JS_FN(js_toJSON_str, str_toString, 0,JSFUN_THISP_STRING),
JS_TN("substring", str_substring, 2,GENERIC_PRIMITIVE, str_substring_trcinfo),

File diff suppressed because it is too large Load Diff

View File

@ -61,9 +61,9 @@ class Queue : public avmplus::GCObject {
T* _data;
unsigned _len;
unsigned _max;
void ensure(unsigned size) {
while (_max < size)
while (_max < size)
_max <<= 1;
_data = (T*)realloc(_data, _max * sizeof(T));
}
@ -73,45 +73,45 @@ public:
this->_len = 0;
this->_data = (T*)malloc(max * sizeof(T));
}
~Queue() {
free(_data);
}
bool contains(T a) {
for (unsigned n = 0; n < _len; ++n)
if (_data[n] == a)
return true;
return false;
}
void add(T a) {
ensure(_len + 1);
JS_ASSERT(_len <= _max);
_data[_len++] = a;
}
void add(T* chunk, unsigned size) {
ensure(_len + size);
JS_ASSERT(_len <= _max);
memcpy(&_data[_len], chunk, size * sizeof(T));
_len += size;
}
void addUnique(T a) {
if (!contains(a))
add(a);
}
void setLength(unsigned len) {
ensure(len + 1);
_len = len;
}
void clear() {
_len = 0;
}
unsigned length() const {
return _len;
}
@ -148,7 +148,7 @@ public:
/*
* The oracle keeps track of slots that should not be demoted to int because we know them
* to overflow or they result in type-unstable traces. We are using a simple hash table.
* to overflow or they result in type-unstable traces. We are using a simple hash table.
* Collisions lead to loss of optimization (demotable slots are not demoted) but have no
* correctness implications.
*/
@ -195,7 +195,7 @@ struct VMSideExit : public nanojit::SideExit
uint32 numStackSlotsBelowCurrentFrame;
ExitType exitType;
};
static inline uint8* getTypeMap(nanojit::SideExit* exit)
{
return (uint8*)(((VMSideExit*)exit) + 1);
@ -243,7 +243,7 @@ public:
struct FrameInfo {
JSObject* callee; // callee function object
jsbytecode* callpc; // pc of JSOP_CALL in caller script
intptr_t ip_adj; // callee script-based pc index and imacro pc
uint8* typemap; // typemap for the stack frame
union {
struct {
@ -253,7 +253,7 @@ struct FrameInfo {
uint32 word; // for spdist/argc LIR store in record_JSOP_CALL
};
};
class TraceRecorder : public avmplus::GCObject {
JSContext* cx;
JSTraceMonitor* traceMonitor;
@ -297,9 +297,9 @@ class TraceRecorder : public avmplus::GCObject {
bool isGlobal(jsval* p) const;
ptrdiff_t nativeGlobalOffset(jsval* p) const;
ptrdiff_t nativeStackOffset(jsval* p) const;
void import(nanojit::LIns* base, ptrdiff_t offset, jsval* p, uint8& t,
void import(nanojit::LIns* base, ptrdiff_t offset, jsval* p, uint8& t,
const char *prefix, uintN index, JSStackFrame *fp);
void import(TreeInfo* treeInfo, nanojit::LIns* sp, unsigned ngslots, unsigned callDepth,
void import(TreeInfo* treeInfo, nanojit::LIns* sp, unsigned ngslots, unsigned callDepth,
uint8* globalTypeMap, uint8* stackTypeMap);
void trackNativeStackUse(unsigned slots);
@ -315,7 +315,7 @@ class TraceRecorder : public avmplus::GCObject {
bool checkType(jsval& v, uint8 t, jsval*& stage_val, nanojit::LIns*& stage_ins,
unsigned& stage_count);
bool deduceTypeStability(nanojit::Fragment* root_peer, nanojit::Fragment** stable_peer,
bool deduceTypeStability(nanojit::Fragment* root_peer, nanojit::Fragment** stable_peer,
unsigned* demotes);
jsval& argval(unsigned n) const;
@ -334,8 +334,10 @@ class TraceRecorder : public avmplus::GCObject {
nanojit::LIns* f2i(nanojit::LIns* f);
nanojit::LIns* makeNumberInt32(nanojit::LIns* f);
nanojit::LIns* stringify(jsval& v, nanojit::LIns* v_ins);
nanojit::LIns* stringify(jsval& v);
bool call_imacro(jsbytecode* imacro);
bool ifop();
bool switchop();
bool inc(jsval& v, jsint incr, bool pre = true);
@ -369,7 +371,7 @@ class TraceRecorder : public avmplus::GCObject {
nanojit::LIns*& dslots_ins, nanojit::LIns* v_ins);
bool native_get(nanojit::LIns* obj_ins, nanojit::LIns* pobj_ins, JSScopeProperty* sprop,
nanojit::LIns*& dslots_ins, nanojit::LIns*& v_ins);
bool name(jsval*& vp);
bool prop(JSObject* obj, nanojit::LIns* obj_ins, uint32& slot, nanojit::LIns*& v_ins);
bool elem(jsval& oval, jsval& idx, jsval*& vp, nanojit::LIns*& v_ins, nanojit::LIns*& addr_ins);
@ -380,28 +382,46 @@ class TraceRecorder : public avmplus::GCObject {
bool box_jsval(jsval v, nanojit::LIns*& v_ins);
bool unbox_jsval(jsval v, nanojit::LIns*& v_ins);
bool guardClass(JSObject* obj, nanojit::LIns* obj_ins, JSClass* clasp);
bool guardDenseArray(JSObject* obj, nanojit::LIns* obj_ins);
bool guardClass(JSObject* obj, nanojit::LIns* obj_ins, JSClass* clasp,
ExitType exitType = MISMATCH_EXIT);
bool guardDenseArray(JSObject* obj, nanojit::LIns* obj_ins,
ExitType exitType = MISMATCH_EXIT);
bool guardDenseArrayIndex(JSObject* obj, jsint idx, nanojit::LIns* obj_ins,
nanojit::LIns* dslots_ins, nanojit::LIns* idx_ins,
nanojit::LIns* dslots_ins, nanojit::LIns* idx_ins,
ExitType exitType);
bool guardElemOp(JSObject* obj, nanojit::LIns* obj_ins, jsid id, size_t op_offset, jsval* vp);
void clearFrameSlotsFromCache();
bool guardShapelessCallee(jsval& callee);
bool interpretedFunctionCall(jsval& fval, JSFunction* fun, uintN argc, bool constructing);
bool functionCall(bool constructing);
bool forInLoop(jsval* vp);
void trackCfgMerges(jsbytecode* pc);
void flipIf(jsbytecode* pc, bool& cond);
void fuseIf(jsbytecode* pc, bool cond, nanojit::LIns* x);
bool hasMethod(JSObject* obj, jsid id);
bool hasToStringMethod(JSObject* obj);
bool hasToStringMethod(jsval v) {
JS_ASSERT(JSVAL_IS_OBJECT(v));
return hasToStringMethod(JSVAL_TO_OBJECT(v));
}
bool hasValueOfMethod(JSObject* obj);
bool hasValueOfMethod(jsval v) {
JS_ASSERT(JSVAL_IS_OBJECT(v));
return hasValueOfMethod(JSVAL_TO_OBJECT(v));
}
bool hasIteratorMethod(JSObject* obj);
bool hasIteratorMethod(jsval v) {
JS_ASSERT(JSVAL_IS_OBJECT(v));
return hasIteratorMethod(JSVAL_TO_OBJECT(v));
}
public:
friend bool js_MonitorRecording(TraceRecorder* tr);
TraceRecorder(JSContext* cx, VMSideExit*, nanojit::Fragment*, TreeInfo*,
unsigned ngslots, uint8* globalTypeMap, uint8* stackTypeMap,
VMSideExit* expectedInnerExit, nanojit::Fragment* outerToBlacklist);
unsigned ngslots, uint8* globalTypeMap, uint8* stackTypeMap,
VMSideExit* expectedInnerExit, nanojit::Fragment* outerToBlacklist);
~TraceRecorder();
uint8 determineSlotType(jsval* vp) const;
@ -418,7 +438,7 @@ public:
void prepareTreeCall(nanojit::Fragment* inner);
void emitTreeCall(nanojit::Fragment* inner, VMSideExit* exit);
unsigned getCallDepth() const;
bool record_EnterFrame();
bool record_LeaveFrame();
bool record_SetPropHit(JSPropCacheEntry* entry, JSScopeProperty* sprop);
@ -426,14 +446,14 @@ public:
bool record_DefLocalFunSetSlot(uint32 slot, JSObject* obj);
bool record_FastNativeCallComplete();
bool record_IteratorNextComplete();
nanojit::Fragment* getOuterToBlacklist() { return outerToBlacklist; }
void deepAbort() { deepAborted = true; }
bool wasDeepAborted() { return deepAborted; }
bool walkedOutOfLoop() { return terminate; }
void setPromotedPeer(nanojit::Fragment* peer) { promotedPeer = peer; }
TreeInfo* getTreeInfo() { return treeInfo; }
#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
bool record_##op();
# include "jsopcode.tbl"
@ -444,29 +464,41 @@ public:
#define TRACE_RECORDER(cx) (JS_TRACE_MONITOR(cx).recorder)
#define SET_TRACE_RECORDER(cx,tr) (JS_TRACE_MONITOR(cx).recorder = (tr))
// See jsinterp.cpp for the ENABLE_TRACER definition.
#define JSOP_IS_BINARY(op) ((uintN)((op) - JSOP_BITOR) <= (uintN)(JSOP_MOD - JSOP_BITOR))
/*
* See jsinterp.cpp for the ENABLE_TRACER definition. Also note how comparing x
* to JSOP_* constants specializes trace-recording code at compile time either
* to include imacro support, or exclude it altogether for this particular x.
*/
#define RECORD_ARGS(x,args) \
JS_BEGIN_MACRO \
if (!js_MonitorRecording(TRACE_RECORDER(cx))) \
if (!js_MonitorRecording(TRACE_RECORDER(cx))) { \
ENABLE_TRACER(0); \
else \
TRACE_ARGS_(TRACE_RECORDER(cx),x,args); \
} else { \
TRACE_ARGS_(x, args, \
if (fp->imacpc && \
(x == JSOP_ITER || x == JSOP_NEXTITER || \
JSOP_IS_BINARY(x))) { \
atoms = COMMON_ATOMS_START(&rt->atomState); \
op = JSOp(*regs.pc); \
DO_OP(); \
} \
); \
} \
JS_END_MACRO
#define TRACE_ARGS_(tr,x,args) \
#define TRACE_ARGS_(x,args,onfalse) \
JS_BEGIN_MACRO \
if (!tr->record_##x args) { \
TraceRecorder* tr_ = TRACE_RECORDER(cx); \
if (tr_ && !tr_->record_##x args) { \
onfalse \
js_AbortRecording(cx, #x); \
ENABLE_TRACER(0); \
} \
JS_END_MACRO
#define TRACE_ARGS(x,args) \
JS_BEGIN_MACRO \
TraceRecorder* tr_ = TRACE_RECORDER(cx); \
if (tr_) \
TRACE_ARGS_(tr_, x, args); \
JS_END_MACRO
#define TRACE_ARGS(x,args) TRACE_ARGS_(x, args, )
#define RECORD(x) RECORD_ARGS(x, ())
#define TRACE_0(x) TRACE_ARGS(x, ())

View File

@ -1881,7 +1881,7 @@ ParseXMLSource(JSContext *cx, JSString *src)
op = (JSOp) *fp->regs->pc;
if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) {
filename = fp->script->filename;
lineno = js_PCToLineNumber(cx, fp->script, fp->regs->pc);
lineno = js_FramePCToLineNumber(cx, fp);
for (endp = srcp + srclen; srcp < endp; srcp++) {
if (*srcp == '\n')
--lineno;

View File

@ -2308,6 +2308,54 @@ function testStringify() {
testStringify.expected = "true,true,false,false,undefined,undefined,5,5,5.5,5.5,x,x";
test(testStringify);
function testObjectToString() {
var o = {toString: function()"foo"};
var s = "";
for (var i = 0; i < 10; i++)
s += o;
return s;
}
testObjectToString.expected = "foofoofoofoofoofoofoofoofoofoo";
test(testObjectToString);
function testObjectToNumber() {
var o = {valueOf: function()-3};
var x = 0;
for (var i = 0; i < 10; i++)
x -= o;
return x;
}
testObjectToNumber.expected = 30;
test(testObjectToNumber);
function my_iterator_next() {
if (this.i == 10) {
this.i = 0;
throw this.StopIteration;
}
return this.i++;
}
function testCustomIterator() {
var o = {
__iterator__: function () {
return {
i: 0,
next: my_iterator_next,
StopIteration: StopIteration
};
}
};
var a=[];
for (var k = 0; k < 100; k += 10) {
for(var j in o) {
a[k + (j >> 0)] = j*k;
}
}
return a.join();
}
testCustomIterator.expected = "0,0,0,0,0,0,0,0,0,0,0,10,20,30,40,50,60,70,80,90,0,20,40,60,80,100,120,140,160,180,0,30,60,90,120,150,180,210,240,270,0,40,80,120,160,200,240,280,320,360,0,50,100,150,200,250,300,350,400,450,0,60,120,180,240,300,360,420,480,540,0,70,140,210,280,350,420,490,560,630,0,80,160,240,320,400,480,560,640,720,0,90,180,270,360,450,540,630,720,810";
test(testCustomIterator);
/* NOTE: Keep this test last, since it screws up all for...in loops after it. */
function testGlobalProtoAccess() {
return "ok";