- Improve global variable performance from 3x slower to 1.2x slower than
locals (169559, r=shaver). - Also fix longstanding bug where global regexps in precompiled scripts were wrongly shared among threads/contexts (165201, r=shaver). - Also fix strict-aliasing gcc warning causes (206599, r=bryner). git-svn-id: svn://10.0.0.236/trunk@154749 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
parent
cc4c57fe36
commit
25a4d6d341
@ -780,7 +780,7 @@ ValueToScript(JSContext *cx, jsval v)
|
||||
fun = JS_ValueToFunction(cx, v);
|
||||
if (!fun)
|
||||
return NULL;
|
||||
script = fun->script;
|
||||
script = FUN_SCRIPT(fun);
|
||||
}
|
||||
return script;
|
||||
}
|
||||
|
||||
@ -90,7 +90,7 @@ MSG_DEF(JSMSG_CANT_UNLOCK, 7, 0, JSEXN_NONE, "can't unlock memory")
|
||||
MSG_DEF(JSMSG_INCOMPATIBLE_PROTO, 8, 3, JSEXN_TYPEERR, "{0}.prototype.{1} called on incompatible {2}")
|
||||
MSG_DEF(JSMSG_NO_CONSTRUCTOR, 9, 1, JSEXN_NONE, "{0} has no constructor")
|
||||
MSG_DEF(JSMSG_CANT_ALIAS, 10, 3, JSEXN_NONE, "can't alias {0} to {1} in class {2}")
|
||||
MSG_DEF(JSMSG_UNUSED11, 11, 0, JSEXN_NONE, "<Error #11 is currently unused>")
|
||||
MSG_DEF(JSMSG_NOT_SCRIPTED_FUNCTION, 11, 1, JSEXN_TYPEERR, "{0} is not a scripted function")
|
||||
MSG_DEF(JSMSG_BAD_SORT_ARG, 12, 0, JSEXN_TYPEERR, "invalid Array.prototype.sort argument")
|
||||
MSG_DEF(JSMSG_BAD_ATOMIC_NUMBER, 13, 1, JSEXN_INTERNALERR, "internal error: no index for atom {0}")
|
||||
MSG_DEF(JSMSG_TOO_MANY_LITERALS, 14, 0, JSEXN_INTERNALERR, "too many literals")
|
||||
|
||||
@ -2820,19 +2820,32 @@ JS_SetCheckObjectAccessCallback(JSRuntime *rt, JSCheckAccessOp acb)
|
||||
return oldacb;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval *vp)
|
||||
static JSBool
|
||||
ReservedSlotIndexOK(JSContext *cx, JSObject *obj, JSClass *clasp,
|
||||
uint32 index, uint32 limit)
|
||||
{
|
||||
JSClass *clasp;
|
||||
uint32 slot;
|
||||
|
||||
CHECK_REQUEST(cx);
|
||||
clasp = OBJ_GET_CLASS(cx, obj);
|
||||
if (index >= JSCLASS_RESERVED_SLOTS(clasp)) {
|
||||
/* Check the computed, possibly per-instance, upper bound. */
|
||||
if (clasp->reserveSlots)
|
||||
JS_LOCK_OBJ_VOID(cx, obj, limit += clasp->reserveSlots(cx, obj));
|
||||
if (index >= limit) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_RESERVED_SLOT_RANGE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval *vp)
|
||||
{
|
||||
JSClass *clasp;
|
||||
uint32 limit, slot;
|
||||
|
||||
CHECK_REQUEST(cx);
|
||||
clasp = OBJ_GET_CLASS(cx, obj);
|
||||
limit = JSCLASS_RESERVED_SLOTS(clasp);
|
||||
if (index >= limit && !ReservedSlotIndexOK(cx, obj, clasp, index, limit))
|
||||
return JS_FALSE;
|
||||
slot = JSSLOT_START(clasp) + index;
|
||||
*vp = OBJ_GET_REQUIRED_SLOT(cx, obj, slot);
|
||||
return JS_TRUE;
|
||||
@ -2842,15 +2855,13 @@ JS_PUBLIC_API(JSBool)
|
||||
JS_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval v)
|
||||
{
|
||||
JSClass *clasp;
|
||||
uint32 slot;
|
||||
uint32 limit, slot;
|
||||
|
||||
CHECK_REQUEST(cx);
|
||||
clasp = OBJ_GET_CLASS(cx, obj);
|
||||
if (index >= JSCLASS_RESERVED_SLOTS(clasp)) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_RESERVED_SLOT_RANGE);
|
||||
limit = JSCLASS_RESERVED_SLOTS(clasp);
|
||||
if (index >= limit && !ReservedSlotIndexOK(cx, obj, clasp, index, limit))
|
||||
return JS_FALSE;
|
||||
}
|
||||
slot = JSSLOT_START(clasp) + index;
|
||||
OBJ_SET_REQUIRED_SLOT(cx, obj, slot, v);
|
||||
return JS_TRUE;
|
||||
|
||||
@ -437,10 +437,11 @@ JS_StringToVersion(const char *string);
|
||||
#define JSOPTION_PRIVATE_IS_NSISUPPORTS \
|
||||
JS_BIT(3) /* context private data points
|
||||
to an nsISupports subclass */
|
||||
#define JSOPTION_COMPILE_N_GO JS_BIT(4) /* one-shot compile+execute,
|
||||
enables compile-time scope
|
||||
chain resolution of consts
|
||||
in many cases, e.g. */
|
||||
#define JSOPTION_COMPILE_N_GO JS_BIT(4) /* caller of JS_Compile*Script
|
||||
promises to execute compiled
|
||||
script once only; enables
|
||||
compile-time scope chain
|
||||
resolution of consts. */
|
||||
|
||||
extern JS_PUBLIC_API(uint32)
|
||||
JS_GetOptions(JSContext *cx);
|
||||
@ -730,7 +731,7 @@ struct JSClass {
|
||||
JSXDRObjectOp xdrObject;
|
||||
JSHasInstanceOp hasInstance;
|
||||
JSMarkOp mark;
|
||||
jsword spare;
|
||||
JSReserveSlotsOp reserveSlots;
|
||||
};
|
||||
|
||||
#define JSCLASS_HAS_PRIVATE (1<<0) /* objects have private slot */
|
||||
@ -751,10 +752,10 @@ struct JSClass {
|
||||
#define JSCLASS_RESERVED_SLOTS_SHIFT 8 /* room for 8 flags below */
|
||||
#define JSCLASS_RESERVED_SLOTS_WIDTH 8 /* and 16 above this field */
|
||||
#define JSCLASS_RESERVED_SLOTS_MASK JS_BITMASK(JSCLASS_RESERVED_SLOTS_WIDTH)
|
||||
#define JSCLASS_HAS_RESERVED_SLOTS(n) (((n) & JSCLASS_RESERVED_SLOTS_MASK) \
|
||||
#define JSCLASS_HAS_RESERVED_SLOTS(n) (((n) & JSCLASS_RESERVED_SLOTS_MASK) \
|
||||
<< JSCLASS_RESERVED_SLOTS_SHIFT)
|
||||
#define JSCLASS_RESERVED_SLOTS(clasp) (((clasp)->flags \
|
||||
>> JSCLASS_RESERVED_SLOTS_SHIFT) \
|
||||
>> JSCLASS_RESERVED_SLOTS_SHIFT) \
|
||||
& JSCLASS_RESERVED_SLOTS_MASK)
|
||||
|
||||
/* Initializer for unused members of statically initialized JSClass structs. */
|
||||
|
||||
@ -191,22 +191,9 @@ js_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp)
|
||||
JSBool b;
|
||||
jsdouble d;
|
||||
|
||||
#if defined _MSC_VER && _MSC_VER <= 800
|
||||
/* MSVC1.5 coredumps */
|
||||
if (!bp)
|
||||
return JS_TRUE;
|
||||
/* This should be an if-else chain, but MSVC1.5 crashes if it is. */
|
||||
#define ELSE
|
||||
#else
|
||||
#define ELSE else
|
||||
#endif
|
||||
|
||||
if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
|
||||
/* Must return early to avoid falling thru to JSVAL_IS_OBJECT case. */
|
||||
*bp = JS_FALSE;
|
||||
return JS_TRUE;
|
||||
}
|
||||
if (JSVAL_IS_OBJECT(v)) {
|
||||
b = JS_FALSE;
|
||||
} else if (JSVAL_IS_OBJECT(v)) {
|
||||
if (!JSVERSION_IS_ECMA(cx->version)) {
|
||||
if (!OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), JSTYPE_BOOLEAN, &v))
|
||||
return JS_FALSE;
|
||||
@ -216,29 +203,18 @@ js_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp)
|
||||
} else {
|
||||
b = JS_TRUE;
|
||||
}
|
||||
} ELSE
|
||||
if (JSVAL_IS_STRING(v)) {
|
||||
} else if (JSVAL_IS_STRING(v)) {
|
||||
b = JSSTRING_LENGTH(JSVAL_TO_STRING(v)) ? JS_TRUE : JS_FALSE;
|
||||
} ELSE
|
||||
if (JSVAL_IS_INT(v)) {
|
||||
} else if (JSVAL_IS_INT(v)) {
|
||||
b = JSVAL_TO_INT(v) ? JS_TRUE : JS_FALSE;
|
||||
} ELSE
|
||||
if (JSVAL_IS_DOUBLE(v)) {
|
||||
} else if (JSVAL_IS_DOUBLE(v)) {
|
||||
d = *JSVAL_TO_DOUBLE(v);
|
||||
b = (!JSDOUBLE_IS_NaN(d) && d != 0) ? JS_TRUE : JS_FALSE;
|
||||
} ELSE
|
||||
#if defined _MSC_VER && _MSC_VER <= 800
|
||||
if (JSVAL_IS_BOOLEAN(v)) {
|
||||
b = JSVAL_TO_BOOLEAN(v);
|
||||
}
|
||||
#else
|
||||
{
|
||||
} else {
|
||||
JS_ASSERT(JSVAL_IS_BOOLEAN(v));
|
||||
b = JSVAL_TO_BOOLEAN(v);
|
||||
}
|
||||
#endif
|
||||
|
||||
#undef ELSE
|
||||
*bp = b;
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
@ -414,7 +414,10 @@ struct JSContext {
|
||||
JSObjectPrincipalsFinder findObjectPrincipals;
|
||||
};
|
||||
|
||||
/* Slightly more readable macros, also to hide bitset implementation detail. */
|
||||
/*
|
||||
* Slightly more readable macros, also to hide bitset implementation detail.
|
||||
* XXX beware non-boolean truth values, which belie the bitset hiding claim!
|
||||
*/
|
||||
#define JS_HAS_STRICT_OPTION(cx) ((cx)->options & JSOPTION_STRICT)
|
||||
#define JS_HAS_WERROR_OPTION(cx) ((cx)->options & JSOPTION_WERROR)
|
||||
#define JS_HAS_COMPILE_N_GO_OPTION(cx) ((cx)->options & JSOPTION_COMPILE_N_GO)
|
||||
|
||||
@ -366,7 +366,7 @@ js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
||||
JSStackFrame frame;
|
||||
|
||||
memset(&frame, 0, sizeof(frame));
|
||||
frame.script = fun->script;
|
||||
frame.script = FUN_SCRIPT(fun);
|
||||
frame.fun = fun;
|
||||
frame.down = cx->fp;
|
||||
cx->fp = &frame;
|
||||
@ -430,8 +430,10 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id,
|
||||
JSAtom *atom;
|
||||
jsid propid;
|
||||
JSObject *pobj;
|
||||
JSProperty *prop;
|
||||
JSScopeProperty *sprop;
|
||||
JSRuntime *rt;
|
||||
JSBool ok;
|
||||
JSWatchPoint *wp;
|
||||
JSPropertyOp watcher;
|
||||
|
||||
@ -451,8 +453,9 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id,
|
||||
propid = (jsid)atom;
|
||||
}
|
||||
|
||||
if (!js_LookupProperty(cx, obj, propid, &pobj, (JSProperty **)&sprop))
|
||||
if (!js_LookupProperty(cx, obj, propid, &pobj, &prop))
|
||||
return JS_FALSE;
|
||||
sprop = (JSScopeProperty *) prop;
|
||||
rt = cx->runtime;
|
||||
if (!sprop) {
|
||||
/* Check for a deleted symbol watchpoint, which holds its property. */
|
||||
@ -461,9 +464,10 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id,
|
||||
/* Make a new property in obj so we can watch for the first set. */
|
||||
if (!js_DefineProperty(cx, obj, propid, JSVAL_VOID,
|
||||
NULL, NULL, JSPROP_ENUMERATE,
|
||||
(JSProperty **)&sprop)) {
|
||||
sprop = NULL;
|
||||
&prop)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
sprop = (JSScopeProperty *) prop;
|
||||
}
|
||||
} else if (pobj != obj) {
|
||||
/* Clone the prototype property so we can watch the right object. */
|
||||
@ -480,36 +484,45 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id,
|
||||
attrs = sprop->attrs;
|
||||
} else {
|
||||
if (!OBJ_GET_PROPERTY(cx, pobj, id, &value)) {
|
||||
OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
|
||||
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
||||
return JS_FALSE;
|
||||
}
|
||||
getter = setter = JS_PropertyStub;
|
||||
attrs = JSPROP_ENUMERATE;
|
||||
}
|
||||
OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
|
||||
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
||||
|
||||
if (!js_DefineProperty(cx, obj, propid, value, getter, setter, attrs,
|
||||
(JSProperty **)&sprop)) {
|
||||
sprop = NULL;
|
||||
&prop)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
sprop = (JSScopeProperty *) prop;
|
||||
}
|
||||
if (!sprop)
|
||||
return JS_FALSE;
|
||||
|
||||
/*
|
||||
* At this point, prop/sprop exists in obj, obj is locked, and we must
|
||||
* OBJ_DROP_PROPERTY(cx, obj, prop) before returning.
|
||||
*/
|
||||
ok = JS_TRUE;
|
||||
wp = FindWatchPoint(rt, OBJ_SCOPE(obj), propid);
|
||||
if (!wp) {
|
||||
watcher = js_WrapWatchedSetter(cx, propid, sprop->attrs, sprop->setter);
|
||||
if (!watcher)
|
||||
return JS_FALSE;
|
||||
if (!watcher) {
|
||||
ok = JS_FALSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
wp = (JSWatchPoint *) JS_malloc(cx, sizeof *wp);
|
||||
if (!wp)
|
||||
return JS_FALSE;
|
||||
if (!wp) {
|
||||
ok = JS_FALSE;
|
||||
goto out;
|
||||
}
|
||||
wp->handler = NULL;
|
||||
wp->closure = NULL;
|
||||
if (!js_AddRoot(cx, &wp->closure, "wp->closure")) {
|
||||
ok = js_AddRoot(cx, &wp->closure, "wp->closure");
|
||||
if (!ok) {
|
||||
JS_free(cx, wp);
|
||||
return JS_FALSE;
|
||||
goto out;
|
||||
}
|
||||
JS_APPEND_LINK(&wp->links, &rt->watchPointList);
|
||||
wp->object = obj;
|
||||
@ -517,15 +530,22 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id,
|
||||
JS_ASSERT(sprop->setter != js_watch_set);
|
||||
wp->setter = sprop->setter;
|
||||
wp->nrefs = 1;
|
||||
|
||||
/* XXXbe nest in obj lock here */
|
||||
sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, 0, sprop->attrs,
|
||||
sprop->getter, watcher);
|
||||
if (!sprop)
|
||||
return DropWatchPoint(cx, wp);
|
||||
if (!sprop) {
|
||||
DropWatchPoint(cx, wp);
|
||||
ok = JS_FALSE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
wp->handler = handler;
|
||||
wp->closure = closure;
|
||||
OBJ_DROP_PROPERTY(cx, obj, (JSProperty *)sprop);
|
||||
return JS_TRUE;
|
||||
|
||||
out:
|
||||
OBJ_DROP_PROPERTY(cx, obj, prop);
|
||||
return ok;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
@ -605,7 +625,7 @@ JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno)
|
||||
JS_PUBLIC_API(JSScript *)
|
||||
JS_GetFunctionScript(JSContext *cx, JSFunction *fun)
|
||||
{
|
||||
return fun->script;
|
||||
return FUN_SCRIPT(fun);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSPrincipals *)
|
||||
@ -1182,8 +1202,8 @@ JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun)
|
||||
obytes = JS_HOWMANY(obytes, fun->nrefs);
|
||||
nbytes += obytes;
|
||||
}
|
||||
if (fun->script)
|
||||
nbytes += JS_GetScriptTotalSize(cx, fun->script);
|
||||
if (fun->interpreted)
|
||||
nbytes += JS_GetScriptTotalSize(cx, fun->u.script);
|
||||
atom = fun->atom;
|
||||
if (atom)
|
||||
nbytes += GetAtomTotalSize(cx, atom);
|
||||
|
||||
@ -59,6 +59,7 @@
|
||||
#include "jsnum.h"
|
||||
#include "jsopcode.h"
|
||||
#include "jsparse.h"
|
||||
#include "jsregexp.h"
|
||||
#include "jsscan.h"
|
||||
#include "jsscope.h"
|
||||
#include "jsscript.h"
|
||||
@ -228,6 +229,7 @@ js_EmitN(JSContext *cx, JSCodeGenerator *cg, JSOp op, size_t extra)
|
||||
|
||||
/* XXX too many "... statement" L10N gaffes below -- fix via js.msg! */
|
||||
const char js_with_statement_str[] = "with statement";
|
||||
const char js_script_str[] = "script";
|
||||
|
||||
static const char *statementName[] = {
|
||||
"block", /* BLOCK */
|
||||
@ -249,7 +251,7 @@ static const char *
|
||||
StatementName(JSCodeGenerator *cg)
|
||||
{
|
||||
if (!cg->treeContext.topStmt)
|
||||
return "script";
|
||||
return js_script_str;
|
||||
return statementName[cg->treeContext.topStmt->type];
|
||||
}
|
||||
|
||||
@ -1496,6 +1498,90 @@ js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom,
|
||||
return ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate an index invariant for all activations of the code being compiled
|
||||
* in cg, that can be used to store and fetch a reference to a cloned RegExp
|
||||
* object prototyped by the object literal in pn->pn_atom. We need clones to
|
||||
* hold lastIndex and other direct properties that should not be shared among
|
||||
* threads sharing a precompiled function or script.
|
||||
*
|
||||
* If the code being compiled is function code, allocate a reserved slot in
|
||||
* the cloned function object that shares the precompiled script with other
|
||||
* cloned function objects and with the compiler-created clone-parent. There
|
||||
* are fun->nregexps such reserved slots in each function object cloned from
|
||||
* fun->object. NB: during compilation, funobj slots must not be allocated,
|
||||
* because js_AllocSlot could hand out one of the slots that should be given
|
||||
* to a regexp clone.
|
||||
*
|
||||
* If the code being compiled is global code, reserve the fp->vars slot at
|
||||
* ALE_INDEX(ale), by ensuring that cg->treeContext.numGlobalVars is at least
|
||||
* one more than this index. For global code, fp->vars is parallel to the
|
||||
* global script->atomMap.vector array, but possibly shorter for the common
|
||||
* case. Global variable name literals in script->atomMap have fast-global
|
||||
* slot numbers (stored as int-tagged jsvals) in the former. The atomIndex
|
||||
* for a regexp object literal thus addresses an fp->vars element not used by
|
||||
* fast-global variables, so we use that GC-scanned entry to keep the regexp
|
||||
* object clone.
|
||||
*
|
||||
* In no case can cx->fp->varobj be a Call object, because that implies we
|
||||
* are compiling eval code, in which case (cx->fp->flags & JSFRAME_EVAL) is
|
||||
* true and js_GetToken will have already selected JSOP_OBJECT instead of
|
||||
* JSOP_REGEXP, to avoid all this RegExp object cloning business.
|
||||
*
|
||||
* Why clone regexp objects? ECMA specifies that when a regular expression
|
||||
* literal is scanned, a RegExp object is created. In the spec, compilation
|
||||
* and execution happen indivisibly, but in this implementation and many of
|
||||
* its embeddings, code is precompiled early and re-executed in multiple
|
||||
* threads, or using multiple global objects, or both, for efficiency.
|
||||
*
|
||||
* In such cases, naively following ECMA leads to wrongful sharing of RegExp
|
||||
* objects, which makes for collisions on the lastIndex property (especially
|
||||
* for global regexps) and on any ad-hoc properties. Also, __proto__ and
|
||||
* __parent__ refer to the pre-compilation prototype and global objects, a
|
||||
* pigeon-hole problem for instanceof tests (although the instanceof operator
|
||||
* implementation for native function objects was hacked a while ago to work
|
||||
* around this problem cleanly).
|
||||
*/
|
||||
static JSBool
|
||||
IndexRegExpClone(JSContext *cx, JSParseNode *pn, JSAtomListElement *ale,
|
||||
JSCodeGenerator *cg)
|
||||
{
|
||||
JSObject *varobj, *reobj;
|
||||
JSClass *clasp;
|
||||
JSFunction *fun;
|
||||
JSRegExp *re;
|
||||
uint16 *countPtr;
|
||||
uintN cloneIndex;
|
||||
|
||||
JS_ASSERT(!(cx->fp->flags & (JSFRAME_EVAL | JSFRAME_COMPILE_N_GO)));
|
||||
|
||||
varobj = cx->fp->varobj;
|
||||
clasp = OBJ_GET_CLASS(cx, varobj);
|
||||
if (clasp == &js_FunctionClass) {
|
||||
fun = (JSFunction *) JS_GetPrivate(cx, varobj);
|
||||
countPtr = &fun->nregexps;
|
||||
cloneIndex = *countPtr;
|
||||
} else {
|
||||
JS_ASSERT(clasp != &js_CallClass);
|
||||
countPtr = &cg->treeContext.numGlobalVars;
|
||||
cloneIndex = ALE_INDEX(ale);
|
||||
}
|
||||
|
||||
if ((cloneIndex + 1) >> 16) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_NEED_DIET, js_script_str);
|
||||
return JS_FALSE;
|
||||
}
|
||||
if (cloneIndex >= *countPtr)
|
||||
*countPtr = cloneIndex + 1;
|
||||
|
||||
reobj = ATOM_TO_OBJECT(pn->pn_atom);
|
||||
JS_ASSERT(OBJ_GET_CLASS(cx, reobj) == &js_RegExpClass);
|
||||
re = (JSRegExp *) JS_GetPrivate(cx, reobj);
|
||||
re->cloneIndex = cloneIndex;
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Emit a bytecode and its 2-byte constant (atom) index immediate operand.
|
||||
* NB: We use cx and cg from our caller's lexical environment, and return
|
||||
@ -1517,6 +1603,8 @@ EmitAtomOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
|
||||
ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList);
|
||||
if (!ale)
|
||||
return JS_FALSE;
|
||||
if (op == JSOP_REGEXP && !IndexRegExpClone(cx, pn, ale, cg))
|
||||
return JS_FALSE;
|
||||
EMIT_ATOM_INDEX_OP(op, ALE_INDEX(ale));
|
||||
return JS_TRUE;
|
||||
}
|
||||
@ -1538,11 +1626,18 @@ EmitAtomOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
|
||||
static JSBool
|
||||
LookupArgOrVar(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
|
||||
{
|
||||
JSStackFrame *fp;
|
||||
JSObject *obj, *pobj;
|
||||
JSClass *clasp;
|
||||
JSBool optimizeGlobals;
|
||||
JSAtom *atom;
|
||||
JSProperty *prop;
|
||||
JSScopeProperty *sprop;
|
||||
JSOp op;
|
||||
JSPropertyOp getter;
|
||||
uintN attrs;
|
||||
jsint slot;
|
||||
JSAtomListElement *ale;
|
||||
|
||||
JS_ASSERT(pn->pn_type == TOK_NAME);
|
||||
if (pn->pn_slot >= 0 || pn->pn_op == JSOP_ARGUMENTS)
|
||||
@ -1560,10 +1655,23 @@ LookupArgOrVar(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
|
||||
* We can't optimize if we're not compiling a function body, whether via
|
||||
* eval, or directly when compiling a function statement or expression.
|
||||
*/
|
||||
obj = cx->fp->varobj;
|
||||
fp = cx->fp;
|
||||
obj = fp->varobj;
|
||||
clasp = OBJ_GET_CLASS(cx, obj);
|
||||
if (clasp != &js_FunctionClass && clasp != &js_CallClass)
|
||||
return JS_TRUE;
|
||||
if (clasp != &js_FunctionClass && clasp != &js_CallClass) {
|
||||
/*
|
||||
* Optimize global variable accesses if there are at least 100 uses
|
||||
* in unambiguous contexts, or failing that, if least half of all the
|
||||
* uses of global vars/consts/functions are in loops.
|
||||
*/
|
||||
optimizeGlobals = (tc->globalUses >= 100 ||
|
||||
(tc->loopyGlobalUses &&
|
||||
tc->loopyGlobalUses >= tc->globalUses / 2));
|
||||
if (!optimizeGlobals)
|
||||
return JS_TRUE;
|
||||
} else {
|
||||
optimizeGlobals = JS_FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* We can't optimize if we're in an eval called inside a with statement,
|
||||
@ -1571,67 +1679,121 @@ LookupArgOrVar(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
|
||||
* block whose exception variable has the same name as pn.
|
||||
*/
|
||||
atom = pn->pn_atom;
|
||||
if (cx->fp->scopeChain != obj ||
|
||||
if (fp->scopeChain != obj ||
|
||||
js_InWithStatement(tc) ||
|
||||
js_InCatchBlock(tc, atom)) {
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ok, we may be able to optimize name to stack slot. Look for an argument
|
||||
* or variable property in the function, or its call object, not found in
|
||||
* any prototype object. Rewrite pn_op and update pn accordingly. NB: We
|
||||
* know that JSOP_DELNAME on an argument or variable must evaluate to
|
||||
* false, due to JSPROP_PERMANENT.
|
||||
*/
|
||||
if (!js_LookupProperty(cx, obj, (jsid)atom, &pobj, (JSProperty **)&sprop))
|
||||
return JS_FALSE;
|
||||
op = pn->pn_op;
|
||||
if (sprop) {
|
||||
if (pobj == obj) {
|
||||
JSPropertyOp getter = sprop->getter;
|
||||
|
||||
if (getter == js_GetArgument) {
|
||||
switch (op) {
|
||||
case JSOP_NAME: op = JSOP_GETARG; break;
|
||||
case JSOP_SETNAME: op = JSOP_SETARG; break;
|
||||
case JSOP_INCNAME: op = JSOP_INCARG; break;
|
||||
case JSOP_NAMEINC: op = JSOP_ARGINC; break;
|
||||
case JSOP_DECNAME: op = JSOP_DECARG; break;
|
||||
case JSOP_NAMEDEC: op = JSOP_ARGDEC; break;
|
||||
case JSOP_FORNAME: op = JSOP_FORARG; break;
|
||||
case JSOP_DELNAME: op = JSOP_FALSE; break;
|
||||
default: JS_ASSERT(0);
|
||||
}
|
||||
} else if (getter == js_GetLocalVariable ||
|
||||
getter == js_GetCallVariable)
|
||||
{
|
||||
switch (op) {
|
||||
case JSOP_NAME: op = JSOP_GETVAR; break;
|
||||
case JSOP_SETNAME: op = JSOP_SETVAR; break;
|
||||
case JSOP_SETCONST: op = JSOP_SETVAR; break;
|
||||
case JSOP_INCNAME: op = JSOP_INCVAR; break;
|
||||
case JSOP_NAMEINC: op = JSOP_VARINC; break;
|
||||
case JSOP_DECNAME: op = JSOP_DECVAR; break;
|
||||
case JSOP_NAMEDEC: op = JSOP_VARDEC; break;
|
||||
case JSOP_FORNAME: op = JSOP_FORVAR; break;
|
||||
case JSOP_DELNAME: op = JSOP_FALSE; break;
|
||||
default: JS_ASSERT(0);
|
||||
}
|
||||
}
|
||||
if (op != pn->pn_op) {
|
||||
pn->pn_op = op;
|
||||
pn->pn_slot = sprop->shortid;
|
||||
}
|
||||
pn->pn_attrs = sprop->attrs;
|
||||
getter = NULL;
|
||||
#ifdef __GNUC__
|
||||
attrs = slot = 0; /* quell GCC overwarning */
|
||||
#endif
|
||||
if (optimizeGlobals) {
|
||||
/*
|
||||
* We are optimizing global variables, and there is no pre-existing
|
||||
* global property named atom. If atom was declared via const or var,
|
||||
* optimize pn to access fp->vars using the appropriate JOF_QVAR op.
|
||||
*/
|
||||
ATOM_LIST_SEARCH(ale, &tc->decls, atom);
|
||||
if (!ale) {
|
||||
/* Use precedes declaration, or name is never declared. */
|
||||
return JS_TRUE;
|
||||
}
|
||||
OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
|
||||
|
||||
attrs = (ALE_JSOP(ale) == JSOP_DEFCONST)
|
||||
? JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT
|
||||
: JSPROP_ENUMERATE | JSPROP_PERMANENT;
|
||||
|
||||
/* Index atom so we can map fast global number to name. */
|
||||
JS_ASSERT(tc->flags & TCF_COMPILING);
|
||||
ale = js_IndexAtom(cx, atom, &((JSCodeGenerator *) tc)->atomList);
|
||||
if (!ale)
|
||||
return JS_FALSE;
|
||||
|
||||
/* Defend against tc->numGlobalVars 16-bit overflow. */
|
||||
slot = ALE_INDEX(ale);
|
||||
if ((slot + 1) >> 16)
|
||||
return JS_TRUE;
|
||||
|
||||
if ((uint16)(slot + 1) > tc->numGlobalVars)
|
||||
tc->numGlobalVars = (uint16)(slot + 1);
|
||||
} else {
|
||||
/*
|
||||
* We may be able to optimize name to stack slot. Look for an argument
|
||||
* or variable property in the function, or its call object, not found
|
||||
* in any prototype object. Rewrite pn_op and update pn accordingly.
|
||||
* NB: We know that JSOP_DELNAME on an argument or variable evaluates
|
||||
* to false, due to JSPROP_PERMANENT.
|
||||
*/
|
||||
if (!js_LookupProperty(cx, obj, (jsid)atom, &pobj, &prop))
|
||||
return JS_FALSE;
|
||||
sprop = (JSScopeProperty *) prop;
|
||||
if (sprop) {
|
||||
if (pobj == obj) {
|
||||
getter = sprop->getter;
|
||||
attrs = sprop->attrs;
|
||||
slot = (sprop->flags & SPROP_HAS_SHORTID) ? sprop->shortid : -1;
|
||||
}
|
||||
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
||||
}
|
||||
}
|
||||
|
||||
if (optimizeGlobals || getter) {
|
||||
if (optimizeGlobals) {
|
||||
switch (op) {
|
||||
case JSOP_NAME: op = JSOP_GETGVAR; break;
|
||||
case JSOP_SETNAME: op = JSOP_SETGVAR; break;
|
||||
case JSOP_SETCONST: /* NB: no change */ break;
|
||||
case JSOP_INCNAME: op = JSOP_INCGVAR; break;
|
||||
case JSOP_NAMEINC: op = JSOP_GVARINC; break;
|
||||
case JSOP_DECNAME: op = JSOP_DECGVAR; break;
|
||||
case JSOP_NAMEDEC: op = JSOP_GVARDEC; break;
|
||||
case JSOP_FORNAME: /* NB: no change */ break;
|
||||
case JSOP_DELNAME: /* NB: no change */ break;
|
||||
default: JS_ASSERT(0);
|
||||
}
|
||||
} else if (getter == js_GetLocalVariable ||
|
||||
getter == js_GetCallVariable) {
|
||||
switch (op) {
|
||||
case JSOP_NAME: op = JSOP_GETVAR; break;
|
||||
case JSOP_SETNAME: op = JSOP_SETVAR; break;
|
||||
case JSOP_SETCONST: op = JSOP_SETVAR; break;
|
||||
case JSOP_INCNAME: op = JSOP_INCVAR; break;
|
||||
case JSOP_NAMEINC: op = JSOP_VARINC; break;
|
||||
case JSOP_DECNAME: op = JSOP_DECVAR; break;
|
||||
case JSOP_NAMEDEC: op = JSOP_VARDEC; break;
|
||||
case JSOP_FORNAME: op = JSOP_FORVAR; break;
|
||||
case JSOP_DELNAME: op = JSOP_FALSE; break;
|
||||
default: JS_ASSERT(0);
|
||||
}
|
||||
} else if (getter == js_GetArgument ||
|
||||
(getter == js_CallClass.getProperty &&
|
||||
fp->fun && (uintN) slot < fp->fun->nargs)) {
|
||||
switch (op) {
|
||||
case JSOP_NAME: op = JSOP_GETARG; break;
|
||||
case JSOP_SETNAME: op = JSOP_SETARG; break;
|
||||
case JSOP_INCNAME: op = JSOP_INCARG; break;
|
||||
case JSOP_NAMEINC: op = JSOP_ARGINC; break;
|
||||
case JSOP_DECNAME: op = JSOP_DECARG; break;
|
||||
case JSOP_NAMEDEC: op = JSOP_ARGDEC; break;
|
||||
case JSOP_FORNAME: op = JSOP_FORARG; break;
|
||||
case JSOP_DELNAME: op = JSOP_FALSE; break;
|
||||
default: JS_ASSERT(0);
|
||||
}
|
||||
}
|
||||
if (op != pn->pn_op) {
|
||||
pn->pn_op = op;
|
||||
pn->pn_slot = slot;
|
||||
}
|
||||
pn->pn_attrs = attrs;
|
||||
}
|
||||
|
||||
if (pn->pn_slot < 0) {
|
||||
/*
|
||||
* We couldn't optimize it, so it's not an arg or local var name. Now
|
||||
* we must check for the predefined arguments variable. It may be
|
||||
* We couldn't optimize pn, so it's not a global or local slot name.
|
||||
* Now we must check for the predefined arguments variable. It may be
|
||||
* overridden by assignment, in which case the function is heavyweight
|
||||
* and the interpreter will look up 'arguments' in the function's call
|
||||
* object.
|
||||
@ -2456,9 +2618,10 @@ js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body,
|
||||
if (!ok)
|
||||
return JS_FALSE;
|
||||
|
||||
fun->script = js_NewScriptFromCG(cx, cg, fun);
|
||||
if (!fun->script)
|
||||
fun->u.script = js_NewScriptFromCG(cx, cg, fun);
|
||||
if (!fun->u.script)
|
||||
return JS_FALSE;
|
||||
fun->interpreted = JS_TRUE;
|
||||
if (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT)
|
||||
fun->flags |= JSFUN_HEAVYWEIGHT;
|
||||
return JS_TRUE;
|
||||
@ -2598,17 +2761,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
#if JS_HAS_LEXICAL_CLOSURE
|
||||
if (cg->treeContext.flags & TCF_IN_FUNCTION) {
|
||||
JSObject *obj, *pobj;
|
||||
JSScopeProperty *sprop;
|
||||
JSProperty *prop;
|
||||
uintN slot;
|
||||
|
||||
obj = OBJ_GET_PARENT(cx, fun->object);
|
||||
if (!js_LookupProperty(cx, obj, (jsid)fun->atom, &pobj,
|
||||
(JSProperty **)&sprop)) {
|
||||
&prop)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
JS_ASSERT(sprop && pobj == obj);
|
||||
slot = sprop->shortid;
|
||||
OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
|
||||
JS_ASSERT(prop && pobj == obj);
|
||||
slot = ((JSScopeProperty *) prop)->shortid;
|
||||
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
||||
|
||||
/* Emit [JSOP_DEFLOCALFUN, local variable slot, atomIndex]. */
|
||||
off = js_EmitN(cx, cg, JSOP_DEFLOCALFUN, VARNO_LEN+ATOM_INDEX_LEN);
|
||||
@ -3400,22 +3563,25 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
if (!ale)
|
||||
return JS_FALSE;
|
||||
atomIndex = ALE_INDEX(ale);
|
||||
|
||||
if (!(cg->treeContext.flags & TCF_IN_FUNCTION) ||
|
||||
(cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT)) {
|
||||
/* Emit a prolog bytecode to predefine the variable. */
|
||||
CG_SWITCH_TO_PROLOG(cg);
|
||||
if (!UpdateLineNumberNotes(cx, cg, pn2))
|
||||
return JS_FALSE;
|
||||
EMIT_ATOM_INDEX_OP(pn->pn_op, atomIndex);
|
||||
CG_SWITCH_TO_MAIN(cg);
|
||||
}
|
||||
}
|
||||
|
||||
if ((js_CodeSpec[op].format & JOF_TYPEMASK) == JOF_CONST &&
|
||||
(!(cg->treeContext.flags & TCF_IN_FUNCTION) ||
|
||||
(cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT))) {
|
||||
/* Emit a prolog bytecode to predefine the variable. */
|
||||
CG_SWITCH_TO_PROLOG(cg);
|
||||
if (!UpdateLineNumberNotes(cx, cg, pn2))
|
||||
return JS_FALSE;
|
||||
EMIT_ATOM_INDEX_OP(pn->pn_op, atomIndex);
|
||||
CG_SWITCH_TO_MAIN(cg);
|
||||
}
|
||||
|
||||
if (pn2->pn_expr) {
|
||||
if (op == JSOP_SETNAME)
|
||||
EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex);
|
||||
pn3 = pn2->pn_expr;
|
||||
if (!js_DefineCompileTimeConstant(cx, cg, pn2->pn_atom,
|
||||
if (pn->pn_op == JSOP_DEFCONST &&
|
||||
!js_DefineCompileTimeConstant(cx, cg, pn2->pn_atom,
|
||||
pn3)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
@ -3501,7 +3667,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
* that it appears useless to the compiler.
|
||||
*/
|
||||
useful = wantval = !cx->fp->fun ||
|
||||
cx->fp->fun->native ||
|
||||
!cx->fp->fun->interpreted ||
|
||||
(cx->fp->flags & JSFRAME_SPECIAL);
|
||||
if (!useful) {
|
||||
if (!CheckSideEffects(cx, &cg->treeContext, pn2, &useful))
|
||||
@ -3644,7 +3810,9 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
switch (pn2->pn_type) {
|
||||
case TOK_NAME:
|
||||
if (pn2->pn_op != JSOP_SETNAME) {
|
||||
EMIT_ATOM_INDEX_OP((pn2->pn_op == JSOP_SETARG)
|
||||
EMIT_ATOM_INDEX_OP((pn2->pn_op == JSOP_SETGVAR)
|
||||
? JSOP_GETGVAR
|
||||
: (pn2->pn_op == JSOP_SETARG)
|
||||
? JSOP_GETARG
|
||||
: JSOP_GETVAR,
|
||||
atomIndex);
|
||||
@ -3852,8 +4020,12 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
return JS_FALSE;
|
||||
op = pn2->pn_op;
|
||||
if (pn2->pn_slot >= 0) {
|
||||
if (pn2->pn_attrs & JSPROP_READONLY)
|
||||
op = JSOP_GETVAR;
|
||||
if (pn2->pn_attrs & JSPROP_READONLY) {
|
||||
/* Incrementing a declared const: just get its value. */
|
||||
op = ((js_CodeSpec[op].format & JOF_TYPEMASK) == JOF_CONST)
|
||||
? JSOP_GETGVAR
|
||||
: JSOP_GETVAR;
|
||||
}
|
||||
atomIndex = (jsatomid) pn2->pn_slot;
|
||||
EMIT_ATOM_INDEX_OP(op, atomIndex);
|
||||
} else {
|
||||
|
||||
@ -93,8 +93,11 @@ struct JSStmtInfo {
|
||||
(stmt)->continues = (stmt)->catchJump = (stmt)->gosub = (-1))
|
||||
|
||||
struct JSTreeContext { /* tree context for semantic checks */
|
||||
uint32 flags; /* statement state flags, see below */
|
||||
uint16 flags; /* statement state flags, see below */
|
||||
uint16 numGlobalVars; /* max. no. of global variables/regexps */
|
||||
uint32 tryCount; /* total count of try statements parsed */
|
||||
uint32 globalUses; /* optimizable global var uses in total */
|
||||
uint32 loopyGlobalUses;/* optimizable global var uses in loops */
|
||||
JSStmtInfo *topStmt; /* top of statement info stack */
|
||||
JSAtomList decls; /* function, const, and var declarations */
|
||||
JSParseNode *nodeList; /* list of recyclable parse-node structs */
|
||||
@ -111,8 +114,10 @@ struct JSTreeContext { /* tree context for semantic checks */
|
||||
#define TCF_FUN_FLAGS 0xE0 /* flags to propagate from FunctionBody */
|
||||
|
||||
#define TREE_CONTEXT_INIT(tc) \
|
||||
((tc)->flags = 0, (tc)->tryCount = 0, (tc)->topStmt = NULL, \
|
||||
ATOM_LIST_INIT(&(tc)->decls), (tc)->nodeList = NULL)
|
||||
((tc)->flags = (tc)->numGlobalVars = 0, \
|
||||
(tc)->tryCount = (tc)->globalUses = (tc)->loopyGlobalUses = 0, \
|
||||
(tc)->topStmt = NULL, ATOM_LIST_INIT(&(tc)->decls), \
|
||||
(tc)->nodeList = NULL)
|
||||
|
||||
#define TREE_CONTEXT_FINISH(tc) \
|
||||
((void)0)
|
||||
|
||||
@ -758,6 +758,7 @@ call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
|
||||
JSString *str;
|
||||
JSAtom *atom;
|
||||
JSObject *obj2;
|
||||
JSProperty *prop;
|
||||
JSScopeProperty *sprop;
|
||||
jsid propid;
|
||||
JSPropertyOp getter, setter;
|
||||
@ -781,17 +782,16 @@ call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
|
||||
atom = js_AtomizeString(cx, str, 0);
|
||||
if (!atom)
|
||||
return JS_FALSE;
|
||||
if (!js_LookupProperty(cx, funobj, (jsid)atom, &obj2,
|
||||
(JSProperty **)&sprop)) {
|
||||
if (!js_LookupProperty(cx, funobj, (jsid)atom, &obj2, &prop))
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
sprop = (JSScopeProperty *) prop;
|
||||
if (sprop && OBJ_IS_NATIVE(obj2)) {
|
||||
propid = sprop->id;
|
||||
getter = sprop->getter;
|
||||
attrs = sprop->attrs & ~JSPROP_SHARED;
|
||||
slot = (uintN) sprop->shortid;
|
||||
OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop);
|
||||
OBJ_DROP_PROPERTY(cx, obj2, prop);
|
||||
if (getter == js_GetArgument || getter == js_GetLocalVariable) {
|
||||
if (getter == js_GetArgument) {
|
||||
vp = fp->argv;
|
||||
@ -868,10 +868,6 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
||||
jsint slot;
|
||||
JSFunction *fun;
|
||||
JSStackFrame *fp;
|
||||
#if defined _MSC_VER &&_MSC_VER <= 800
|
||||
/* MSVC1.5 coredumps */
|
||||
jsval bogus = *vp;
|
||||
#endif
|
||||
|
||||
if (!JSVAL_IS_INT(id))
|
||||
return JS_TRUE;
|
||||
@ -942,10 +938,6 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
||||
default:
|
||||
/* XXX fun[0] and fun.arguments[0] are equivalent. */
|
||||
if (fp && fp->fun && (uintN)slot < fp->fun->nargs)
|
||||
#if defined _MSC_VER &&_MSC_VER <= 800
|
||||
/* MSVC1.5 coredumps */
|
||||
if (bogus == *vp)
|
||||
#endif
|
||||
*vp = fp->argv[slot];
|
||||
break;
|
||||
}
|
||||
@ -1056,8 +1048,8 @@ fun_finalize(JSContext *cx, JSObject *obj)
|
||||
JS_ATOMIC_DECREMENT(&fun->nrefs);
|
||||
if (fun->nrefs)
|
||||
return;
|
||||
if (fun->script)
|
||||
js_DestroyScript(cx, fun->script);
|
||||
if (fun->interpreted)
|
||||
js_DestroyScript(cx, fun->u.script);
|
||||
JS_free(cx, fun);
|
||||
}
|
||||
|
||||
@ -1078,6 +1070,7 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp)
|
||||
JSContext *cx;
|
||||
JSFunction *fun;
|
||||
JSString *atomstr;
|
||||
uint32 flagsword; /* originally only flags was JS_XDRUint8'd */
|
||||
char *propname;
|
||||
JSScopeProperty *sprop;
|
||||
uint32 userid; /* NB: holds a signed int-tagged jsval */
|
||||
@ -1098,7 +1091,14 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp)
|
||||
fun = (JSFunction *) JS_GetPrivate(cx, *objp);
|
||||
if (!fun)
|
||||
return JS_TRUE;
|
||||
if (!fun->interpreted) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_NOT_SCRIPTED_FUNCTION,
|
||||
JS_GetFunctionName(fun));
|
||||
return JS_FALSE;
|
||||
}
|
||||
atomstr = fun->atom ? ATOM_TO_STRING(fun->atom) : NULL;
|
||||
flagsword = ((uint32)fun->nregexps << 16) | fun->flags;
|
||||
} else {
|
||||
fun = js_NewFunction(cx, NULL, NULL, 0, 0, NULL, NULL);
|
||||
if (!fun)
|
||||
@ -1110,7 +1110,7 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp)
|
||||
!JS_XDRUint16(xdr, &fun->nargs) ||
|
||||
!JS_XDRUint16(xdr, &fun->extra) ||
|
||||
!JS_XDRUint16(xdr, &fun->nvars) ||
|
||||
!JS_XDRUint8(xdr, &fun->flags)) {
|
||||
!JS_XDRUint32(xdr, &flagsword)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
@ -1215,25 +1215,23 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp)
|
||||
}
|
||||
}
|
||||
|
||||
if (!js_XDRScript(xdr, &fun->script, NULL))
|
||||
if (!js_XDRScript(xdr, &fun->u.script, NULL))
|
||||
return JS_FALSE;
|
||||
|
||||
if (xdr->mode == JSXDR_DECODE) {
|
||||
fun->interpreted = JS_TRUE;
|
||||
fun->flags = (uint8) flagsword;
|
||||
fun->nregexps = (uint16) (flagsword >> 16);
|
||||
|
||||
*objp = fun->object;
|
||||
if (atomstr) {
|
||||
/* XXX only if this was a top-level function! */
|
||||
fun->atom = js_AtomizeString(cx, atomstr, 0);
|
||||
if (!fun->atom)
|
||||
return JS_FALSE;
|
||||
|
||||
if (!OBJ_DEFINE_PROPERTY(cx, cx->globalObject,
|
||||
(jsid)fun->atom, OBJECT_TO_JSVAL(*objp),
|
||||
NULL, NULL, JSPROP_ENUMERATE,
|
||||
NULL)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
js_CallNewScriptHook(cx, fun->script, fun);
|
||||
js_CallNewScriptHook(cx, fun->u.script, fun);
|
||||
}
|
||||
|
||||
return JS_TRUE;
|
||||
@ -1312,8 +1310,10 @@ fun_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
|
||||
if (JSVAL_IS_FUNCTION(cx, cval)) {
|
||||
cfun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(cval));
|
||||
ofun = (JSFunction *) JS_GetPrivate(cx, obj);
|
||||
if (cfun->native == ofun->native &&
|
||||
cfun->script == ofun->script) {
|
||||
if (cfun->interpreted == ofun->interpreted &&
|
||||
(cfun->interpreted
|
||||
? cfun->u.script == ofun->u.script
|
||||
: cfun->u.native == ofun->u.native)) {
|
||||
*bp = JS_TRUE;
|
||||
break;
|
||||
}
|
||||
@ -1339,12 +1339,21 @@ fun_mark(JSContext *cx, JSObject *obj, void *arg)
|
||||
if (fun) {
|
||||
if (fun->atom)
|
||||
GC_MARK_ATOM(cx, fun->atom, arg);
|
||||
if (fun->script)
|
||||
js_MarkScript(cx, fun->script, arg);
|
||||
if (fun->interpreted)
|
||||
js_MarkScript(cx, fun->u.script, arg);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32
|
||||
fun_reserveSlots(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JSFunction *fun;
|
||||
|
||||
fun = (JSFunction *) JS_GetPrivate(cx, obj);
|
||||
return fun ? fun->nregexps : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reserve two slots in all function objects for XPConnect. Note that this
|
||||
* does not bloat every instance, only those on which reserved slots are set,
|
||||
@ -1360,7 +1369,7 @@ JSClass js_FunctionClass = {
|
||||
NULL, NULL,
|
||||
NULL, NULL,
|
||||
fun_xdrObject, fun_hasInstance,
|
||||
fun_mark, 0
|
||||
fun_mark, fun_reserveSlots
|
||||
};
|
||||
|
||||
JSBool
|
||||
@ -1619,6 +1628,7 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
JSAtom *atom;
|
||||
const char *filename;
|
||||
JSObject *obj2;
|
||||
JSProperty *prop;
|
||||
JSScopeProperty *sprop;
|
||||
JSString *str, *arg;
|
||||
void *mark;
|
||||
@ -1672,7 +1682,7 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
* are built for Function.prototype.call or .apply activations that invoke
|
||||
* Function indirectly from a script.
|
||||
*/
|
||||
JS_ASSERT(!fp->script && fp->fun && fp->fun->native == Function);
|
||||
JS_ASSERT(!fp->script && fp->fun && fp->fun->u.native == Function);
|
||||
caller = JS_GetScriptedCaller(cx, fp);
|
||||
if (caller) {
|
||||
filename = caller->script->filename;
|
||||
@ -1760,10 +1770,9 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
* we're assured at this point that it's a valid identifier.
|
||||
*/
|
||||
atom = CURRENT_TOKEN(ts).t_atom;
|
||||
if (!js_LookupProperty(cx, obj, (jsid)atom, &obj2,
|
||||
(JSProperty **)&sprop)) {
|
||||
if (!js_LookupProperty(cx, obj, (jsid)atom, &obj2, &prop))
|
||||
goto bad_formal;
|
||||
}
|
||||
sprop = (JSScopeProperty *) prop;
|
||||
dupflag = 0;
|
||||
if (sprop) {
|
||||
ok = JS_TRUE;
|
||||
@ -1786,7 +1795,7 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
|
||||
dupflag = SPROP_IS_DUPLICATE;
|
||||
}
|
||||
OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop);
|
||||
OBJ_DROP_PROPERTY(cx, obj2, prop);
|
||||
if (!ok)
|
||||
goto bad_formal;
|
||||
sprop = NULL;
|
||||
@ -1882,9 +1891,10 @@ js_InitFunctionClass(JSContext *cx, JSObject *obj)
|
||||
fun = js_NewFunction(cx, proto, NULL, 0, 0, obj, NULL);
|
||||
if (!fun)
|
||||
goto bad;
|
||||
fun->script = js_NewScript(cx, 0, 0, 0);
|
||||
if (!fun->script)
|
||||
fun->u.script = js_NewScript(cx, 0, 0, 0);
|
||||
if (!fun->u.script)
|
||||
goto bad;
|
||||
fun->interpreted = JS_TRUE;
|
||||
return proto;
|
||||
|
||||
bad:
|
||||
@ -1926,12 +1936,13 @@ js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs,
|
||||
/* Initialize all function members. */
|
||||
fun->nrefs = 0;
|
||||
fun->object = NULL;
|
||||
fun->native = native;
|
||||
fun->script = NULL;
|
||||
fun->u.native = native;
|
||||
fun->nargs = nargs;
|
||||
fun->extra = 0;
|
||||
fun->nvars = 0;
|
||||
fun->flags = flags & JSFUN_FLAGS_MASK;
|
||||
fun->interpreted = JS_FALSE;
|
||||
fun->nregexps = 0;
|
||||
fun->spare = 0;
|
||||
fun->atom = atom;
|
||||
fun->clasp = NULL;
|
||||
|
||||
@ -50,17 +50,24 @@ JS_BEGIN_EXTERN_C
|
||||
struct JSFunction {
|
||||
jsrefcount nrefs; /* number of referencing objects */
|
||||
JSObject *object; /* back-pointer to GC'ed object header */
|
||||
JSNative native; /* native method pointer or null */
|
||||
JSScript *script; /* interpreted bytecode descriptor or null */
|
||||
union {
|
||||
JSNative native; /* native method pointer or null */
|
||||
JSScript *script; /* interpreted bytecode descriptor or null */
|
||||
} u;
|
||||
uint16 nargs; /* minimum number of actual arguments */
|
||||
uint16 extra; /* number of arg slots for local GC roots */
|
||||
uint16 nvars; /* number of local variables */
|
||||
uint8 flags; /* bound method and other flags, see jsapi.h */
|
||||
uint8 spare; /* reserved for future use */
|
||||
JSPackedBool interpreted; /* use u.script if true, u.native if false */
|
||||
uint16 nregexps; /* number of regular expressions literals */
|
||||
uint16 spare; /* reserved for future use */
|
||||
JSAtom *atom; /* name for diagnostics and decompiling */
|
||||
JSClass *clasp; /* if non-null, constructor for this class */
|
||||
};
|
||||
|
||||
#define FUN_NATIVE(fun) ((fun)->interpreted ? NULL : (fun)->u.native)
|
||||
#define FUN_SCRIPT(fun) ((fun)->interpreted ? (fun)->u.script : NULL)
|
||||
|
||||
extern JSClass js_ArgumentsClass;
|
||||
extern JSClass js_CallClass;
|
||||
|
||||
|
||||
@ -1288,6 +1288,9 @@ restart:
|
||||
GC_MARK_JSVALS(cx, sh->nslots, JS_STACK_SEGMENT(sh), "stack");
|
||||
}
|
||||
}
|
||||
#ifdef DUMP_CALL_TABLE
|
||||
js_DumpCallTable(cx);
|
||||
#endif
|
||||
|
||||
if (rt->gcCallback)
|
||||
(void) rt->gcCallback(cx, JSGC_MARK_END);
|
||||
|
||||
@ -158,15 +158,15 @@ struct GCMarkNode {
|
||||
GCMarkNode *prev;
|
||||
};
|
||||
|
||||
#define GC_MARK(_cx, _thing, _name, _prev) \
|
||||
#define GC_MARK(cx_, thing_, name_, prev_) \
|
||||
JS_BEGIN_MACRO \
|
||||
GCMarkNode _node; \
|
||||
_node.thing = _thing; \
|
||||
_node.name = _name; \
|
||||
_node.next = NULL; \
|
||||
_node.prev = _prev; \
|
||||
if (_prev) ((GCMarkNode *)(_prev))->next = &_node; \
|
||||
js_MarkGCThing(_cx, _thing, &_node); \
|
||||
GCMarkNode node_; \
|
||||
node_.thing = thing_; \
|
||||
node_.name = name_; \
|
||||
node_.next = NULL; \
|
||||
node_.prev = prev_; \
|
||||
if (prev_) ((GCMarkNode *)(prev_))->next = &node_; \
|
||||
js_MarkGCThing(cx_, thing_, &node_); \
|
||||
JS_END_MACRO
|
||||
|
||||
#else /* !GC_MARK_DEBUG */
|
||||
@ -180,7 +180,7 @@ struct GCMarkNode {
|
||||
* GC_KEEP_ATOMS Don't sweep unmarked atoms, they may be in use by the
|
||||
* compiler, or by an API function that calls js_Atomize,
|
||||
* when the GC is called from js_AllocGCThing, due to a
|
||||
* malloc failure or runtime GC-thing limit.
|
||||
* malloc failure or the runtime GC-thing limit.
|
||||
* GC_LAST_CONTEXT Called from js_DestroyContext for last JSContext in a
|
||||
* JSRuntime, when it is imperative that rt->gcPoke gets
|
||||
* cleared early in js_GC, if it is set.
|
||||
|
||||
@ -112,22 +112,16 @@ JS_NewHashTable(uint32 n, JSHashFunction keyHash,
|
||||
|
||||
if (!allocOps) allocOps = &defaultHashAllocOps;
|
||||
|
||||
ht = (JSHashTable*) (*allocOps->allocTable)(allocPriv, sizeof *ht);
|
||||
ht = (JSHashTable*) allocOps->allocTable(allocPriv, sizeof *ht);
|
||||
if (!ht)
|
||||
return NULL;
|
||||
memset(ht, 0, sizeof *ht);
|
||||
ht->shift = JS_HASH_BITS - n;
|
||||
n = JS_BIT(n);
|
||||
#if defined _MSC_VER && _MSC_VER <= 800
|
||||
if (n > 16000) {
|
||||
(*allocOps->freeTable)(allocPriv, ht);
|
||||
return NULL;
|
||||
}
|
||||
#endif /* WIN16 */
|
||||
nb = n * sizeof(JSHashEntry *);
|
||||
ht->buckets = (JSHashEntry**) (*allocOps->allocTable)(allocPriv, nb);
|
||||
ht->buckets = (JSHashEntry**) allocOps->allocTable(allocPriv, nb);
|
||||
if (!ht->buckets) {
|
||||
(*allocOps->freeTable)(allocPriv, ht);
|
||||
allocOps->freeTable(allocPriv, ht);
|
||||
return NULL;
|
||||
}
|
||||
memset(ht->buckets, 0, nb);
|
||||
@ -153,17 +147,17 @@ JS_HashTableDestroy(JSHashTable *ht)
|
||||
hep = &ht->buckets[i];
|
||||
while ((he = *hep) != NULL) {
|
||||
*hep = he->next;
|
||||
(*allocOps->freeEntry)(allocPriv, he, HT_FREE_ENTRY);
|
||||
allocOps->freeEntry(allocPriv, he, HT_FREE_ENTRY);
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG
|
||||
memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]);
|
||||
#endif
|
||||
(*allocOps->freeTable)(allocPriv, ht->buckets);
|
||||
allocOps->freeTable(allocPriv, ht->buckets);
|
||||
#ifdef DEBUG
|
||||
memset(ht, 0xDB, sizeof *ht);
|
||||
#endif
|
||||
(*allocOps->freeTable)(allocPriv, ht);
|
||||
allocOps->freeTable(allocPriv, ht);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -182,7 +176,7 @@ JS_HashTableRawLookup(JSHashTable *ht, JSHashNumber keyHash, const void *key)
|
||||
h >>= ht->shift;
|
||||
hep = hep0 = &ht->buckets[h];
|
||||
while ((he = *hep) != NULL) {
|
||||
if (he->keyHash == keyHash && (*ht->keyCompare)(key, he->key)) {
|
||||
if (he->keyHash == keyHash && ht->keyCompare(key, he->key)) {
|
||||
/* Move to front of chain if not already there */
|
||||
if (hep != hep0) {
|
||||
*hep = he->next;
|
||||
@ -211,12 +205,9 @@ JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **hep,
|
||||
n = NBUCKETS(ht);
|
||||
if (ht->nentries >= OVERLOADED(n)) {
|
||||
oldbuckets = ht->buckets;
|
||||
#if defined _MSC_VER && _MSC_VER <= 800
|
||||
if (2 * n > 16000)
|
||||
return NULL;
|
||||
#endif /* WIN16 */
|
||||
nb = 2 * n * sizeof(JSHashEntry *);
|
||||
ht->buckets = (JSHashEntry**) (*ht->allocOps->allocTable)(ht->allocPriv, nb);
|
||||
ht->buckets = (JSHashEntry**)
|
||||
ht->allocOps->allocTable(ht->allocPriv, nb);
|
||||
if (!ht->buckets) {
|
||||
ht->buckets = oldbuckets;
|
||||
return NULL;
|
||||
@ -239,12 +230,12 @@ JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **hep,
|
||||
#ifdef DEBUG
|
||||
memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);
|
||||
#endif
|
||||
(*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets);
|
||||
ht->allocOps->freeTable(ht->allocPriv, oldbuckets);
|
||||
hep = JS_HashTableRawLookup(ht, keyHash, key);
|
||||
}
|
||||
|
||||
/* Make a new key value entry */
|
||||
he = (*ht->allocOps->allocEntry)(ht->allocPriv, key);
|
||||
he = ht->allocOps->allocEntry(ht->allocPriv, key);
|
||||
if (!he)
|
||||
return NULL;
|
||||
he->keyHash = keyHash;
|
||||
@ -262,16 +253,16 @@ JS_HashTableAdd(JSHashTable *ht, const void *key, void *value)
|
||||
JSHashNumber keyHash;
|
||||
JSHashEntry *he, **hep;
|
||||
|
||||
keyHash = (*ht->keyHash)(key);
|
||||
keyHash = ht->keyHash(key);
|
||||
hep = JS_HashTableRawLookup(ht, keyHash, key);
|
||||
if ((he = *hep) != NULL) {
|
||||
/* Hit; see if values match */
|
||||
if ((*ht->valueCompare)(he->value, value)) {
|
||||
if (ht->valueCompare(he->value, value)) {
|
||||
/* key,value pair is already present in table */
|
||||
return he;
|
||||
}
|
||||
if (he->value)
|
||||
(*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_VALUE);
|
||||
ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_VALUE);
|
||||
he->value = value;
|
||||
return he;
|
||||
}
|
||||
@ -286,14 +277,15 @@ JS_HashTableRawRemove(JSHashTable *ht, JSHashEntry **hep, JSHashEntry *he)
|
||||
size_t nb;
|
||||
|
||||
*hep = he->next;
|
||||
(*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_ENTRY);
|
||||
ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_ENTRY);
|
||||
|
||||
/* Shrink table if it's underloaded */
|
||||
n = NBUCKETS(ht);
|
||||
if (--ht->nentries < UNDERLOADED(n)) {
|
||||
oldbuckets = ht->buckets;
|
||||
nb = n * sizeof(JSHashEntry*) / 2;
|
||||
ht->buckets = (JSHashEntry**) (*ht->allocOps->allocTable)(ht->allocPriv, nb);
|
||||
ht->buckets = (JSHashEntry**)
|
||||
ht->allocOps->allocTable(ht->allocPriv, nb);
|
||||
if (!ht->buckets) {
|
||||
ht->buckets = oldbuckets;
|
||||
return;
|
||||
@ -316,7 +308,7 @@ JS_HashTableRawRemove(JSHashTable *ht, JSHashEntry **hep, JSHashEntry *he)
|
||||
#ifdef DEBUG
|
||||
memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);
|
||||
#endif
|
||||
(*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets);
|
||||
ht->allocOps->freeTable(ht->allocPriv, oldbuckets);
|
||||
}
|
||||
}
|
||||
|
||||
@ -326,7 +318,7 @@ JS_HashTableRemove(JSHashTable *ht, const void *key)
|
||||
JSHashNumber keyHash;
|
||||
JSHashEntry *he, **hep;
|
||||
|
||||
keyHash = (*ht->keyHash)(key);
|
||||
keyHash = ht->keyHash(key);
|
||||
hep = JS_HashTableRawLookup(ht, keyHash, key);
|
||||
if ((he = *hep) == NULL)
|
||||
return JS_FALSE;
|
||||
@ -342,7 +334,7 @@ JS_HashTableLookup(JSHashTable *ht, const void *key)
|
||||
JSHashNumber keyHash;
|
||||
JSHashEntry *he, **hep;
|
||||
|
||||
keyHash = (*ht->keyHash)(key);
|
||||
keyHash = ht->keyHash(key);
|
||||
hep = JS_HashTableRawLookup(ht, keyHash, key);
|
||||
if ((he = *hep) != NULL) {
|
||||
return he->value;
|
||||
@ -367,7 +359,7 @@ JS_HashTableEnumerateEntries(JSHashTable *ht, JSHashEnumerator f, void *arg)
|
||||
for (i = 0; i < nbuckets; i++) {
|
||||
hep = &ht->buckets[i];
|
||||
while ((he = *hep) != NULL) {
|
||||
rv = (*f)(he, n, arg);
|
||||
rv = f(he, n, arg);
|
||||
n++;
|
||||
if (rv & (HT_ENUMERATE_REMOVE | HT_ENUMERATE_UNHASH)) {
|
||||
*hep = he->next;
|
||||
@ -443,7 +435,7 @@ JS_HashTableDumpMeter(JSHashTable *ht, JSHashEnumerator dump, FILE *fp)
|
||||
fprintf(fp, " max hash chain: [%u]\n", maxChain);
|
||||
|
||||
for (he = ht->buckets[maxChain], i = 0; he; he = he->next, i++)
|
||||
if ((*dump)(he, i, fp) != HT_ENUMERATE_NEXT)
|
||||
if (dump(he, i, fp) != HT_ENUMERATE_NEXT)
|
||||
break;
|
||||
}
|
||||
#endif /* HASHMETER */
|
||||
|
||||
@ -626,6 +626,337 @@ ComputeThis(JSContext *cx, JSObject *thisp, JSStackFrame *fp)
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
#ifdef DUMP_CALL_TABLE
|
||||
|
||||
#include "jsclist.h"
|
||||
#include "jshash.h"
|
||||
#include "jsdtoa.h"
|
||||
|
||||
typedef struct CallKey {
|
||||
jsval callee; /* callee value */
|
||||
const char *filename; /* function filename or null */
|
||||
uintN lineno; /* function lineno or 0 */
|
||||
} CallKey;
|
||||
|
||||
/* Compensate for typeof null == "object" brain damage. */
|
||||
#define JSTYPE_NULL JSTYPE_LIMIT
|
||||
#define TYPEOF(cx,v) (JSVAL_IS_NULL(v) ? JSTYPE_NULL : JS_TypeOfValue(cx,v))
|
||||
#define TYPENAME(t) (((t) == JSTYPE_NULL) ? js_null_str : js_type_str[t])
|
||||
#define NTYPEHIST (JSTYPE_LIMIT + 1)
|
||||
|
||||
typedef struct CallValue {
|
||||
uint32 total; /* total call count */
|
||||
uint32 recycled; /* LRU-recycled calls lost */
|
||||
uint16 minargc; /* minimum argument count */
|
||||
uint16 maxargc; /* maximum argument count */
|
||||
struct ArgInfo {
|
||||
uint32 typeHist[NTYPEHIST]; /* histogram by type */
|
||||
JSCList lruList; /* top 10 values LRU list */
|
||||
struct ArgValCount {
|
||||
JSCList lruLink; /* LRU list linkage */
|
||||
jsval value; /* recently passed value */
|
||||
uint32 count; /* number of times passed */
|
||||
char strbuf[112]; /* string conversion buffer */
|
||||
} topValCounts[10]; /* top 10 value storage */
|
||||
} argInfo[8];
|
||||
} CallValue;
|
||||
|
||||
typedef struct CallEntry {
|
||||
JSHashEntry entry;
|
||||
CallKey key;
|
||||
CallValue value;
|
||||
char name[32]; /* function name copy */
|
||||
} CallEntry;
|
||||
|
||||
static void *
|
||||
AllocCallTable(void *pool, size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
static void
|
||||
FreeCallTable(void *pool, void *item)
|
||||
{
|
||||
free(item);
|
||||
}
|
||||
|
||||
static JSHashEntry *
|
||||
AllocCallEntry(void *pool, const void *key)
|
||||
{
|
||||
return (JSHashEntry*) calloc(1, sizeof(CallEntry));
|
||||
}
|
||||
|
||||
static void
|
||||
FreeCallEntry(void *pool, JSHashEntry *he, uintN flag)
|
||||
{
|
||||
JS_ASSERT(flag == HT_FREE_ENTRY);
|
||||
free(he);
|
||||
}
|
||||
|
||||
static JSHashAllocOps callTableAllocOps = {
|
||||
AllocCallTable, FreeCallTable,
|
||||
AllocCallEntry, FreeCallEntry
|
||||
};
|
||||
|
||||
JS_STATIC_DLL_CALLBACK(JSHashNumber)
|
||||
js_hash_call_key(const void *key)
|
||||
{
|
||||
CallKey *ck = (CallKey *) key;
|
||||
JSHashNumber hash = (jsuword)ck->callee >> 3;
|
||||
|
||||
if (ck->filename) {
|
||||
hash = (hash << 4) ^ JS_HashString(ck->filename);
|
||||
hash = (hash << 4) ^ ck->lineno;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
JS_STATIC_DLL_CALLBACK(intN)
|
||||
js_compare_call_keys(const void *k1, const void *k2)
|
||||
{
|
||||
CallKey *ck1 = (CallKey *)k1, *ck2 = (CallKey *)k2;
|
||||
|
||||
return ck1->callee == ck2->callee &&
|
||||
((ck1->filename && ck2->filename)
|
||||
? strcmp(ck1->filename, ck2->filename) == 0
|
||||
: ck1->filename == ck2->filename) &&
|
||||
ck1->lineno == ck2->lineno;
|
||||
}
|
||||
|
||||
JSHashTable *js_CallTable;
|
||||
size_t js_LogCallToSourceLimit;
|
||||
|
||||
JS_STATIC_DLL_CALLBACK(intN)
|
||||
CallTableDumper(JSHashEntry *he, intN k, void *arg)
|
||||
{
|
||||
CallEntry *ce = (CallEntry *)he;
|
||||
FILE *fp = (FILE *)arg;
|
||||
uintN argc, i, n;
|
||||
struct ArgInfo *ai;
|
||||
JSType save, type;
|
||||
JSCList *cl;
|
||||
struct ArgValCount *avc;
|
||||
jsval argval;
|
||||
|
||||
if (ce->key.filename) {
|
||||
/* We're called at the end of the mark phase, so mark our filenames. */
|
||||
js_MarkScriptFilename(ce->key.filename);
|
||||
fprintf(fp, "%s:%u ", ce->key.filename, ce->key.lineno);
|
||||
} else {
|
||||
fprintf(fp, "@%p ", (void *) ce->key.callee);
|
||||
}
|
||||
|
||||
if (ce->name[0])
|
||||
fprintf(fp, "name %s ", ce->name);
|
||||
fprintf(fp, "calls %lu (%lu) argc %u/%u\n",
|
||||
(unsigned long) ce->value.total,
|
||||
(unsigned long) ce->value.recycled,
|
||||
ce->value.minargc, ce->value.maxargc);
|
||||
|
||||
argc = JS_MIN(ce->value.maxargc, 8);
|
||||
for (i = 0; i < argc; i++) {
|
||||
ai = &ce->value.argInfo[i];
|
||||
|
||||
n = 0;
|
||||
save = -1;
|
||||
for (type = JSTYPE_VOID; type <= JSTYPE_LIMIT; type++) {
|
||||
if (ai->typeHist[type]) {
|
||||
save = type;
|
||||
++n;
|
||||
}
|
||||
}
|
||||
if (n == 1) {
|
||||
fprintf(fp, " arg %u type %s: %lu\n",
|
||||
i, TYPENAME(save), (unsigned long) ai->typeHist[save]);
|
||||
} else {
|
||||
fprintf(fp, " arg %u type histogram:\n", i);
|
||||
for (type = JSTYPE_VOID; type <= JSTYPE_LIMIT; type++) {
|
||||
fprintf(fp, " %9s: %8lu ",
|
||||
TYPENAME(type), (unsigned long) ai->typeHist[type]);
|
||||
for (n = (uintN) JS_HOWMANY(ai->typeHist[type], 10); n > 0; --n)
|
||||
fputc('*', fp);
|
||||
fputc('\n', fp);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(fp, " arg %u top 10 values:\n", i);
|
||||
n = 1;
|
||||
for (cl = ai->lruList.prev; cl != &ai->lruList; cl = cl->prev) {
|
||||
avc = (struct ArgValCount *)cl;
|
||||
if (!avc->count)
|
||||
break;
|
||||
argval = avc->value;
|
||||
fprintf(fp, " %9u: %8lu %.*s (%#lx)\n",
|
||||
n, (unsigned long) avc->count,
|
||||
sizeof avc->strbuf, avc->strbuf, argval);
|
||||
++n;
|
||||
}
|
||||
}
|
||||
|
||||
return HT_ENUMERATE_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
js_DumpCallTable(JSContext *cx)
|
||||
{
|
||||
char name[24];
|
||||
FILE *fp;
|
||||
static uintN dumpCount;
|
||||
|
||||
if (!js_CallTable)
|
||||
return;
|
||||
|
||||
JS_snprintf(name, sizeof name, "/tmp/calltable.dump.%u", dumpCount & 7);
|
||||
dumpCount++;
|
||||
fp = fopen(name, "w");
|
||||
if (!fp)
|
||||
return;
|
||||
|
||||
JS_HashTableEnumerateEntries(js_CallTable, CallTableDumper, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
static void
|
||||
LogCall(JSContext *cx, jsval callee, uintN argc, jsval *argv)
|
||||
{
|
||||
CallKey key;
|
||||
const char *name, *cstr;
|
||||
JSFunction *fun;
|
||||
JSHashNumber keyHash;
|
||||
JSHashEntry **hep, *he;
|
||||
CallEntry *ce;
|
||||
uintN i, j;
|
||||
jsval argval;
|
||||
JSType type;
|
||||
struct ArgInfo *ai;
|
||||
struct ArgValCount *avc;
|
||||
JSString *str;
|
||||
|
||||
if (!js_CallTable) {
|
||||
js_CallTable = JS_NewHashTable(1024, js_hash_call_key,
|
||||
js_compare_call_keys, NULL,
|
||||
&callTableAllocOps, NULL);
|
||||
if (!js_CallTable)
|
||||
return;
|
||||
}
|
||||
|
||||
key.callee = callee;
|
||||
key.filename = NULL;
|
||||
key.lineno = 0;
|
||||
name = "";
|
||||
if (JSVAL_IS_FUNCTION(cx, callee)) {
|
||||
fun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(callee));
|
||||
if (fun->atom)
|
||||
name = js_AtomToPrintableString(cx, fun->atom);
|
||||
if (fun->interpreted) {
|
||||
key.filename = fun->u.script->filename;
|
||||
key.lineno = fun->u.script->lineno;
|
||||
}
|
||||
}
|
||||
keyHash = js_hash_call_key(&key);
|
||||
|
||||
hep = JS_HashTableRawLookup(js_CallTable, keyHash, &key);
|
||||
he = *hep;
|
||||
if (he) {
|
||||
ce = (CallEntry *) he;
|
||||
JS_ASSERT(strncmp(ce->name, name, sizeof ce->name) == 0);
|
||||
} else {
|
||||
he = JS_HashTableRawAdd(js_CallTable, hep, keyHash, &key, NULL);
|
||||
if (!he)
|
||||
return;
|
||||
ce = (CallEntry *) he;
|
||||
ce->entry.key = &ce->key;
|
||||
ce->entry.value = &ce->value;
|
||||
ce->key = key;
|
||||
for (i = 0; i < 8; i++) {
|
||||
ai = &ce->value.argInfo[i];
|
||||
JS_INIT_CLIST(&ai->lruList);
|
||||
for (j = 0; j < 10; j++)
|
||||
JS_APPEND_LINK(&ai->topValCounts[j].lruLink, &ai->lruList);
|
||||
}
|
||||
strncpy(ce->name, name, sizeof ce->name);
|
||||
}
|
||||
|
||||
++ce->value.total;
|
||||
if (ce->value.minargc < argc)
|
||||
ce->value.minargc = argc;
|
||||
if (ce->value.maxargc < argc)
|
||||
ce->value.maxargc = argc;
|
||||
if (argc > 8)
|
||||
argc = 8;
|
||||
for (i = 0; i < argc; i++) {
|
||||
ai = &ce->value.argInfo[i];
|
||||
argval = argv[i];
|
||||
type = TYPEOF(cx, argval);
|
||||
++ai->typeHist[type];
|
||||
|
||||
for (j = 0; ; j++) {
|
||||
if (j == 10) {
|
||||
avc = (struct ArgValCount *) ai->lruList.next;
|
||||
ce->value.recycled += avc->count;
|
||||
avc->value = argval;
|
||||
avc->count = 1;
|
||||
break;
|
||||
}
|
||||
avc = &ai->topValCounts[j];
|
||||
if (avc->value == argval) {
|
||||
++avc->count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Move avc to the back of the LRU list. */
|
||||
JS_REMOVE_LINK(&avc->lruLink);
|
||||
JS_APPEND_LINK(&avc->lruLink, &ai->lruList);
|
||||
|
||||
str = NULL;
|
||||
cstr = "";
|
||||
switch (TYPEOF(cx, argval)) {
|
||||
case JSTYPE_VOID:
|
||||
cstr = js_type_str[JSTYPE_VOID];
|
||||
break;
|
||||
case JSTYPE_NULL:
|
||||
cstr = js_null_str;
|
||||
break;
|
||||
case JSTYPE_BOOLEAN:
|
||||
cstr = js_boolean_str[JSVAL_TO_BOOLEAN(argval)];
|
||||
break;
|
||||
case JSTYPE_NUMBER:
|
||||
if (JSVAL_IS_INT(argval)) {
|
||||
JS_snprintf(avc->strbuf, sizeof avc->strbuf, "%ld",
|
||||
JSVAL_TO_INT(argval));
|
||||
} else {
|
||||
JS_dtostr(avc->strbuf, sizeof avc->strbuf, DTOSTR_STANDARD, 0,
|
||||
*JSVAL_TO_DOUBLE(argval));
|
||||
}
|
||||
continue;
|
||||
case JSTYPE_STRING:
|
||||
str = js_QuoteString(cx, JSVAL_TO_STRING(argval), (jschar)'"');
|
||||
break;
|
||||
case JSTYPE_FUNCTION:
|
||||
if (JSVAL_IS_FUNCTION(cx, argval)) {
|
||||
fun = (JSFunction *)JS_GetPrivate(cx, JSVAL_TO_OBJECT(argval));
|
||||
if (fun && fun->atom) {
|
||||
str = ATOM_TO_STRING(fun->atom);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* FALL THROUGH */
|
||||
case JSTYPE_OBJECT:
|
||||
js_LogCallToSourceLimit = sizeof avc->strbuf;
|
||||
cx->options |= JSOPTION_LOGCALL_TOSOURCE;
|
||||
str = js_ValueToSource(cx, argval);
|
||||
cx->options &= ~JSOPTION_LOGCALL_TOSOURCE;
|
||||
break;
|
||||
}
|
||||
if (str)
|
||||
cstr = JS_GetStringBytes(str);
|
||||
strncpy(avc->strbuf, cstr, sizeof avc->strbuf);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* DUMP_CALL_TABLE */
|
||||
|
||||
/*
|
||||
* Find a function reference and its 'this' object implicit first parameter
|
||||
* under argc arguments on cx's stack, and call the function. Push missing
|
||||
@ -753,7 +1084,8 @@ js_Invoke(JSContext *cx, uintN argc, uintN flags)
|
||||
* the small number of words not already allocated as part
|
||||
* of the caller's operand stack.
|
||||
*/
|
||||
JS_ArenaCountAllocation(pool, (jsuword)sp - a->avail);
|
||||
JS_ArenaCountAllocation(&cx->stackPool,
|
||||
(jsuword)sp - a->avail);
|
||||
a->avail = (jsuword)sp;
|
||||
}
|
||||
}
|
||||
@ -782,7 +1114,10 @@ js_Invoke(JSContext *cx, uintN argc, uintN flags)
|
||||
ops = funobj->map->ops;
|
||||
|
||||
/*
|
||||
* XXX
|
||||
* XXX this makes no sense -- why convert to function if clasp->call?
|
||||
* XXX better to call that hook without converting
|
||||
* XXX the only thing that needs fixing is liveconnect
|
||||
*
|
||||
* Try converting to function, for closure and API compatibility.
|
||||
* We attempt the conversion under all circumstances for 1.2, but
|
||||
* only if there is a call op defined otherwise.
|
||||
@ -813,8 +1148,13 @@ js_Invoke(JSContext *cx, uintN argc, uintN flags)
|
||||
have_fun:
|
||||
/* Get private data and set derived locals from it. */
|
||||
fun = (JSFunction *) JS_GetPrivate(cx, funobj);
|
||||
native = fun->native;
|
||||
script = fun->script;
|
||||
if (fun->interpreted) {
|
||||
native = NULL;
|
||||
script = fun->u.script;
|
||||
} else {
|
||||
native = fun->u.native;
|
||||
script = NULL;
|
||||
}
|
||||
minargs = fun->nargs + fun->extra;
|
||||
nvars = fun->nvars;
|
||||
|
||||
@ -941,6 +1281,9 @@ have_fun:
|
||||
ok = native(cx, frame.thisp, argc, frame.argv, &frame.rval);
|
||||
JS_RUNTIME_METER(cx->runtime, nativeCalls);
|
||||
} else if (script) {
|
||||
#ifdef DUMP_CALL_TABLE
|
||||
LogCall(cx, *vp, argc, frame.argv);
|
||||
#endif
|
||||
/* Use parent scope so js_GetCallObject can find the right "Call". */
|
||||
frame.scopeChain = parent;
|
||||
if (fun->flags & JSFUN_HEAVYWEIGHT) {
|
||||
@ -1069,7 +1412,7 @@ js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval,
|
||||
JS_ASSERT(mode == JSACC_READ || mode == JSACC_WRITE);
|
||||
if (cx->runtime->checkObjectAccess &&
|
||||
JSVAL_IS_FUNCTION(cx, fval) &&
|
||||
((JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(fval)))->script &&
|
||||
((JSFunction *)JS_GetPrivate(cx, JSVAL_TO_OBJECT(fval)))->interpreted &&
|
||||
!cx->runtime->checkObjectAccess(cx, obj, ID_TO_VALUE(id), mode,
|
||||
&fval)) {
|
||||
return JS_FALSE;
|
||||
@ -1080,16 +1423,16 @@ js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval,
|
||||
|
||||
JSBool
|
||||
js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
|
||||
JSStackFrame *down, uintN special, jsval *result)
|
||||
JSStackFrame *down, uintN flags, jsval *result)
|
||||
{
|
||||
JSInterpreterHook hook;
|
||||
void *hookData, *mark;
|
||||
JSStackFrame *oldfp, frame;
|
||||
JSObject *obj, *tmp;
|
||||
JSBool ok;
|
||||
JSInterpreterHook hook;
|
||||
void *hookData;
|
||||
|
||||
hook = cx->runtime->executeHook;
|
||||
hookData = NULL;
|
||||
hookData = mark = NULL;
|
||||
oldfp = cx->fp;
|
||||
frame.callobj = frame.argsobj = NULL;
|
||||
frame.script = script;
|
||||
@ -1113,8 +1456,17 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
|
||||
frame.varobj = obj;
|
||||
frame.fun = NULL;
|
||||
frame.thisp = chain;
|
||||
frame.argc = frame.nvars = 0;
|
||||
frame.argv = frame.vars = NULL;
|
||||
frame.argc = 0;
|
||||
frame.argv = NULL;
|
||||
frame.nvars = script->numGlobalVars;
|
||||
if (frame.nvars) {
|
||||
frame.vars = js_AllocRawStack(cx, frame.nvars, &mark);
|
||||
if (!frame.vars)
|
||||
return JS_FALSE;
|
||||
memset(frame.vars, 0, frame.nvars * sizeof(jsval));
|
||||
} else {
|
||||
frame.vars = NULL;
|
||||
}
|
||||
frame.annotation = NULL;
|
||||
frame.sharpArray = NULL;
|
||||
}
|
||||
@ -1125,7 +1477,7 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
|
||||
frame.sp = oldfp ? oldfp->sp : NULL;
|
||||
frame.spbase = NULL;
|
||||
frame.sharpDepth = 0;
|
||||
frame.flags = special;
|
||||
frame.flags = flags;
|
||||
frame.dormantNext = NULL;
|
||||
frame.objAtomMap = NULL;
|
||||
|
||||
@ -1159,6 +1511,8 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
|
||||
if (hook)
|
||||
hook(cx, &frame, JS_FALSE, &ok, hookData);
|
||||
}
|
||||
if (mark)
|
||||
js_FreeRawStack(cx, mark);
|
||||
cx->fp = oldfp;
|
||||
|
||||
if (oldfp && oldfp != down) {
|
||||
@ -1282,11 +1636,10 @@ out:
|
||||
|
||||
JSBool
|
||||
js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
|
||||
JSBool *foundp)
|
||||
JSObject **objp, JSProperty **propp)
|
||||
{
|
||||
JSObject *obj2;
|
||||
JSProperty *prop;
|
||||
JSBool ok;
|
||||
uintN oldAttrs, report;
|
||||
JSBool isFunction;
|
||||
jsval value;
|
||||
@ -1294,13 +1647,16 @@ js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
|
||||
|
||||
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
|
||||
return JS_FALSE;
|
||||
*foundp = (prop != NULL);
|
||||
if (propp) {
|
||||
*objp = obj2;
|
||||
*propp = prop;
|
||||
}
|
||||
if (!prop)
|
||||
return JS_TRUE;
|
||||
ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs);
|
||||
OBJ_DROP_PROPERTY(cx, obj2, prop);
|
||||
if (!ok)
|
||||
return JS_FALSE;
|
||||
|
||||
/* From here, return true, or goto bad on failure to drop prop. */
|
||||
if (!OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs))
|
||||
goto bad;
|
||||
|
||||
/* If either property is readonly, we have an error. */
|
||||
report = ((oldAttrs | attrs) & JSPROP_READONLY)
|
||||
@ -1326,7 +1682,7 @@ js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
|
||||
isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0;
|
||||
if (!isFunction) {
|
||||
if (!OBJ_GET_PROPERTY(cx, obj, id, &value))
|
||||
return JS_FALSE;
|
||||
goto bad;
|
||||
isFunction = JSVAL_IS_FUNCTION(cx, value);
|
||||
}
|
||||
type = (oldAttrs & attrs & JSPROP_GETTER)
|
||||
@ -1340,18 +1696,24 @@ js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
|
||||
: js_var_str;
|
||||
name = js_AtomToPrintableString(cx, (JSAtom *)id);
|
||||
if (!name)
|
||||
return JS_FALSE;
|
||||
goto bad;
|
||||
return JS_ReportErrorFlagsAndNumber(cx, report,
|
||||
js_GetErrorMessage, NULL,
|
||||
JSMSG_REDECLARED_VAR,
|
||||
type, name);
|
||||
|
||||
bad:
|
||||
if (propp) {
|
||||
*objp = NULL;
|
||||
*propp = NULL;
|
||||
}
|
||||
OBJ_DROP_PROPERTY(cx, obj2, prop);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
#ifndef MAX_INTERP_LEVEL
|
||||
#if defined(XP_OS2)
|
||||
#define MAX_INTERP_LEVEL 250
|
||||
#elif defined _MSC_VER && _MSC_VER <= 800
|
||||
#define MAX_INTERP_LEVEL 30
|
||||
#else
|
||||
#define MAX_INTERP_LEVEL 1000
|
||||
#endif
|
||||
@ -2710,7 +3072,7 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
* yet converted. As above, the expression result goes in rtmp, the updated
|
||||
* value goes in rval.
|
||||
*/
|
||||
#define NONINT_INCREMENT_OP() \
|
||||
#define NONINT_INCREMENT_OP_MIDDLE() \
|
||||
JS_BEGIN_MACRO \
|
||||
VALUE_TO_NUMBER(cx, rval, d); \
|
||||
if (cs->format & JOF_POST) { \
|
||||
@ -2731,7 +3093,7 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
goto out; \
|
||||
JS_END_MACRO
|
||||
|
||||
NONINT_INCREMENT_OP();
|
||||
NONINT_INCREMENT_OP_MIDDLE();
|
||||
}
|
||||
|
||||
CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval));
|
||||
@ -2746,7 +3108,7 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
* loop created by the JS_BEGIN/END_MACRO brackets.
|
||||
*/
|
||||
#define FAST_INCREMENT_OP(SLOT,COUNT,BASE,PRE,OP,MINMAX) \
|
||||
slot = (uintN)SLOT; \
|
||||
slot = SLOT; \
|
||||
JS_ASSERT(slot < fp->fun->COUNT); \
|
||||
vp = fp->BASE + slot; \
|
||||
rval = *vp; \
|
||||
@ -2781,11 +3143,49 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
#undef FAST_INCREMENT_OP
|
||||
|
||||
do_nonint_fast_incop:
|
||||
NONINT_INCREMENT_OP();
|
||||
NONINT_INCREMENT_OP_MIDDLE();
|
||||
*vp = rval;
|
||||
PUSH_OPND(rtmp);
|
||||
break;
|
||||
|
||||
#define FAST_GLOBAL_INCREMENT_OP(SLOWOP,PRE,OP,MINMAX) \
|
||||
slot = GET_VARNO(pc); \
|
||||
JS_ASSERT(slot < fp->nvars); \
|
||||
lval = fp->vars[slot]; \
|
||||
if (JSVAL_IS_NULL(lval)) { \
|
||||
op = SLOWOP; \
|
||||
goto do_op; \
|
||||
} \
|
||||
slot = JSVAL_TO_INT(lval); \
|
||||
obj = fp->varobj; \
|
||||
rval = OBJ_GET_SLOT(cx, obj, slot); \
|
||||
if (JSVAL_IS_INT(rval) && \
|
||||
rval != INT_TO_JSVAL(JSVAL_INT_##MINMAX)) { \
|
||||
PRE = rval; \
|
||||
rval OP 2; \
|
||||
OBJ_SET_SLOT(cx, obj, slot, rval); \
|
||||
PUSH_OPND(PRE); \
|
||||
break; \
|
||||
} \
|
||||
goto do_nonint_fast_global_incop;
|
||||
|
||||
case JSOP_INCGVAR:
|
||||
FAST_GLOBAL_INCREMENT_OP(JSOP_INCNAME, rval, +=, MAX);
|
||||
case JSOP_DECGVAR:
|
||||
FAST_GLOBAL_INCREMENT_OP(JSOP_DECNAME, rval, -=, MIN);
|
||||
case JSOP_GVARINC:
|
||||
FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEINC, rtmp, +=, MAX);
|
||||
case JSOP_GVARDEC:
|
||||
FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEDEC, rtmp, -=, MIN);
|
||||
|
||||
#undef FAST_GLOBAL_INCREMENT_OP
|
||||
|
||||
do_nonint_fast_global_incop:
|
||||
NONINT_INCREMENT_OP_MIDDLE();
|
||||
OBJ_SET_SLOT(cx, obj, slot, rval);
|
||||
PUSH_OPND(rtmp);
|
||||
break;
|
||||
|
||||
case JSOP_GETPROP:
|
||||
/* Get an immediate atom naming the property. */
|
||||
atom = GET_ATOM(cx, script, pc);
|
||||
@ -2860,7 +3260,7 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
if (JSVAL_IS_FUNCTION(cx, lval) &&
|
||||
(obj = JSVAL_TO_OBJECT(lval),
|
||||
fun = (JSFunction *) JS_GetPrivate(cx, obj),
|
||||
!fun->native &&
|
||||
fun->interpreted &&
|
||||
!(fun->flags & (JSFUN_HEAVYWEIGHT | JSFUN_BOUND_METHOD)) &&
|
||||
argc >= (uintN)(fun->nargs + fun->extra)))
|
||||
/* inline_call: */
|
||||
@ -2889,7 +3289,7 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
nframeslots = (sizeof(JSInlineFrame) + sizeof(jsval) - 1)
|
||||
/ sizeof(jsval);
|
||||
nvars = fun->nvars;
|
||||
script = fun->script;
|
||||
script = fun->u.script;
|
||||
depth = (jsint) script->depth;
|
||||
|
||||
/* Allocate the frame and space for vars and operands. */
|
||||
@ -2921,6 +3321,9 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
js_FreeRawStack(cx, newmark);
|
||||
goto bad_inline_call;
|
||||
}
|
||||
#ifdef DUMP_CALL_TABLE
|
||||
LogCall(cx, *vp, argc, vp + 2);
|
||||
#endif
|
||||
|
||||
/* Push void to initialize local variables. */
|
||||
sp = newsp;
|
||||
@ -3073,48 +3476,75 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
|
||||
case JSOP_NUMBER:
|
||||
case JSOP_STRING:
|
||||
case JSOP_OBJECT:
|
||||
atom = GET_ATOM(cx, script, pc);
|
||||
PUSH_OPND(ATOM_KEY(atom));
|
||||
obj = NULL;
|
||||
break;
|
||||
|
||||
case JSOP_OBJECT:
|
||||
case JSOP_REGEXP:
|
||||
{
|
||||
#if 0
|
||||
jsatomid atomIndex;
|
||||
JSAtomMap *atomMap;
|
||||
JSRegExp *re;
|
||||
JSObject *funobj;
|
||||
|
||||
/*
|
||||
* Get a suitable object from an atom mapped by the bytecode at pc.
|
||||
* Push a regexp object for the atom mapped by the bytecode at pc,
|
||||
* cloning the literal's regexp object if necessary, to simulate in
|
||||
* the pre-compile/execute-later case what ECMA specifies for the
|
||||
* compile-and-go case: that scanning each regexp literal creates
|
||||
* a single corresponding RegExp object.
|
||||
*
|
||||
* We must handle the case where a regexp object literal is used in
|
||||
* a different global at execution time from the global with which
|
||||
* it was scanned at compile time, in order to rewrap the JSRegExp
|
||||
* struct with a new object having the right prototype and parent.
|
||||
* To support pre-compilation transparently, we must handle the
|
||||
* case where a regexp object literal is used in a different global
|
||||
* at execution time from the global with which it was scanned at
|
||||
* compile time. We do this by re-wrapping the JSRegExp private
|
||||
* data struct with a cloned object having the right prototype and
|
||||
* parent, and having its own lastIndex property value storage.
|
||||
*
|
||||
* Unlike JSOP_DEFFUN and other prolog bytecodes, we don't want to
|
||||
* pay a script prolog execution price for all regexp literals in a
|
||||
* script (many may not be used by a particular execution of that
|
||||
* script, depending on control flow), so we do all fp->objAtomMap
|
||||
* initialization lazily, here under JSOP_OBJECT.
|
||||
* Unlike JSOP_DEFFUN and other prolog bytecodes that may clone
|
||||
* literal objects, we don't want to pay a script prolog execution
|
||||
* price for all regexp literals in a script (many may not be used
|
||||
* by a particular execution of that script, depending on control
|
||||
* flow), so we initialize lazily here.
|
||||
*
|
||||
* XXX This code is specific to regular expression objects. If we
|
||||
* need JSOP_OBJECT for other kinds of object literals, we should
|
||||
* push cloning down under JSObjectOps. Also, fp->objAtomMap is
|
||||
* used only for object atoms, so it's sparse (wasting some stack
|
||||
* space) and as its name implies, you can't get non-object atoms
|
||||
* from it.
|
||||
* need a similar op for other kinds of object literals, we should
|
||||
* push cloning down under JSObjectOps and reuse code here.
|
||||
*/
|
||||
atomIndex = GET_ATOM_INDEX(pc);
|
||||
atomMap = fp->objAtomMap;
|
||||
atom = atomMap ? atomMap->vector[atomIndex] : NULL;
|
||||
if (!atom) {
|
||||
/* Let atom and obj denote the regexp (object) mapped by pc. */
|
||||
atom = js_GetAtom(cx, &script->atomMap, atomIndex);
|
||||
JS_ASSERT(ATOM_IS_OBJECT(atom));
|
||||
obj = ATOM_TO_OBJECT(atom);
|
||||
JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_RegExpClass);
|
||||
atom = GET_ATOM(cx, script, pc);
|
||||
JS_ASSERT(ATOM_IS_OBJECT(atom));
|
||||
obj = ATOM_TO_OBJECT(atom);
|
||||
JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_RegExpClass);
|
||||
|
||||
re = (JSRegExp *) JS_GetPrivate(cx, obj);
|
||||
slot = re->cloneIndex;
|
||||
if (fp->fun) {
|
||||
/*
|
||||
* We're in function code, not global or eval code (in eval
|
||||
* code, JSOP_REGEXP is never emitted). The code generator
|
||||
* recorded in fp->fun->nregexps the number of re->cloneIndex
|
||||
* slots that it reserved in the cloned funobj.
|
||||
*/
|
||||
funobj = JSVAL_TO_OBJECT(fp->argv[-2]);
|
||||
slot += JSCLASS_RESERVED_SLOTS(&js_FunctionClass);
|
||||
if (!JS_GetReservedSlot(cx, funobj, slot, &rval))
|
||||
return JS_FALSE;
|
||||
if (JSVAL_IS_VOID(rval))
|
||||
rval = JSVAL_NULL;
|
||||
} else {
|
||||
/*
|
||||
* We're in global code. The code generator already arranged
|
||||
* via script->numGlobalVars to reserve a global variable slot
|
||||
* at cloneIndex. All global variable slots are initialized
|
||||
* to null, not void, for faster testing in JSOP_*GVAR cases.
|
||||
*/
|
||||
rval = fp->vars[slot];
|
||||
#ifdef __GNUC__
|
||||
funobj = NULL; /* suppress bogus gcc warnings */
|
||||
#endif
|
||||
}
|
||||
|
||||
if (JSVAL_IS_NULL(rval)) {
|
||||
/* Compute the current global object in obj2. */
|
||||
obj2 = fp->scopeChain;
|
||||
while ((parent = OBJ_GET_PARENT(cx, obj2)) != NULL)
|
||||
@ -3138,8 +3568,11 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
* and you get what you deserve).
|
||||
*
|
||||
* This same coupling between instance parent and constructor
|
||||
* parent turns up elsewhere (see jsobj.c's FindConstructor,
|
||||
* js_ConstructObject, and js_NewObject). It's fundamental.
|
||||
* parent turns up everywhere (see jsobj.c's FindConstructor,
|
||||
* js_ConstructObject, and js_NewObject). It's fundamental to
|
||||
* the design of the language when you consider multiple global
|
||||
* objects and separate compilation and execution, even though
|
||||
* it is not specified fully in ECMA.
|
||||
*/
|
||||
if (OBJ_GET_PARENT(cx, obj) != obj2) {
|
||||
obj = js_CloneRegExpObject(cx, obj, obj2);
|
||||
@ -3147,46 +3580,18 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
ok = JS_FALSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
atom = js_AtomizeObject(cx, obj, 0);
|
||||
if (!atom) {
|
||||
ok = JS_FALSE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
rval = OBJECT_TO_JSVAL(obj);
|
||||
|
||||
/*
|
||||
* If fp->objAtomMap is null, initialize it now so we can map
|
||||
* atom (whether atom is newly created for a cloned object, or
|
||||
* the original atom mapped by script) for faster performance
|
||||
* next time through JSOP_OBJECT.
|
||||
*/
|
||||
if (!atomMap) {
|
||||
jsatomid mapLength = script->atomMap.length;
|
||||
size_t vectorBytes = mapLength * sizeof(JSAtom *);
|
||||
|
||||
/* Allocate an override atom map from cx->stackPool. */
|
||||
JS_ARENA_ALLOCATE_CAST(atomMap, JSAtomMap *,
|
||||
&cx->stackPool,
|
||||
sizeof(JSAtomMap) + vectorBytes);
|
||||
if (!atomMap) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
ok = JS_FALSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
atomMap->length = mapLength;
|
||||
atomMap->vector = (JSAtom **)(atomMap + 1);
|
||||
memset(atomMap->vector, 0, vectorBytes);
|
||||
fp->objAtomMap = atomMap;
|
||||
/* Store the regexp object value in its cloneIndex slot. */
|
||||
if (fp->fun) {
|
||||
if (!JS_SetReservedSlot(cx, funobj, slot, rval))
|
||||
return JS_FALSE;
|
||||
} else {
|
||||
fp->vars[slot] = rval;
|
||||
}
|
||||
atomMap->vector[atomIndex] = atom;
|
||||
}
|
||||
#else
|
||||
atom = GET_ATOM(cx, script, pc);
|
||||
JS_ASSERT(ATOM_IS_OBJECT(atom));
|
||||
#endif
|
||||
rval = ATOM_KEY(atom);
|
||||
|
||||
PUSH_OPND(rval);
|
||||
obj = NULL;
|
||||
break;
|
||||
@ -3560,12 +3965,53 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
obj = NULL;
|
||||
break;
|
||||
|
||||
case JSOP_GETGVAR:
|
||||
slot = GET_VARNO(pc);
|
||||
JS_ASSERT(slot < fp->nvars);
|
||||
lval = fp->vars[slot];
|
||||
if (JSVAL_IS_NULL(lval)) {
|
||||
op = JSOP_NAME;
|
||||
goto do_op;
|
||||
}
|
||||
slot = JSVAL_TO_INT(lval);
|
||||
obj = fp->varobj;
|
||||
rval = OBJ_GET_SLOT(cx, obj, slot);
|
||||
PUSH_OPND(rval);
|
||||
break;
|
||||
|
||||
case JSOP_SETGVAR:
|
||||
slot = GET_VARNO(pc);
|
||||
JS_ASSERT(slot < fp->nvars);
|
||||
rval = FETCH_OPND(-1);
|
||||
lval = fp->vars[slot];
|
||||
obj = fp->varobj;
|
||||
if (JSVAL_IS_NULL(lval)) {
|
||||
/*
|
||||
* Inline-clone and specialize JSOP_SETNAME code here because
|
||||
* JSOP_SETGVAR has arity 1: [rval], not arity 2: [obj, rval]
|
||||
* as JSOP_SETNAME does, where [obj] is due to JSOP_BINDNAME.
|
||||
*/
|
||||
atom = GET_ATOM(cx, script, pc);
|
||||
id = (jsid)atom;
|
||||
SAVE_SP(fp);
|
||||
CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval));
|
||||
if (!ok)
|
||||
goto out;
|
||||
} else {
|
||||
slot = JSVAL_TO_INT(lval);
|
||||
GC_POKE(cx, obj->slots[slot]);
|
||||
OBJ_SET_SLOT(cx, obj, slot, rval);
|
||||
}
|
||||
STORE_OPND(-1, rval);
|
||||
break;
|
||||
|
||||
case JSOP_DEFCONST:
|
||||
case JSOP_DEFVAR:
|
||||
{
|
||||
JSBool defined;
|
||||
jsatomid atomIndex;
|
||||
|
||||
atom = GET_ATOM(cx, script, pc);
|
||||
atomIndex = GET_ATOM_INDEX(pc);
|
||||
atom = js_GetAtom(cx, &script->atomMap, atomIndex);
|
||||
obj = fp->varobj;
|
||||
attrs = JSPROP_ENUMERATE;
|
||||
if (!(fp->flags & JSFRAME_EVAL))
|
||||
@ -3575,25 +4021,55 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
|
||||
/* Lookup id in order to check for redeclaration problems. */
|
||||
id = (jsid)atom;
|
||||
ok = js_CheckRedeclaration(cx, obj, id, attrs, &defined);
|
||||
ok = js_CheckRedeclaration(cx, obj, id, attrs, &obj2, &prop);
|
||||
if (!ok)
|
||||
goto out;
|
||||
|
||||
/* Bind a variable only if it's not yet defined. */
|
||||
if (!defined) {
|
||||
if (!prop) {
|
||||
ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL,
|
||||
attrs, NULL);
|
||||
attrs, &prop);
|
||||
if (!ok)
|
||||
goto out;
|
||||
JS_ASSERT(prop);
|
||||
obj2 = obj;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to optimize a property we either just created, or found
|
||||
* directly in the global object, that is permanent, has a slot,
|
||||
* and has stub getter and setter, into a "fast global" accessed
|
||||
* by the JSOP_*GVAR opcodes.
|
||||
*/
|
||||
if (script->numGlobalVars &&
|
||||
(attrs & JSPROP_PERMANENT) &&
|
||||
obj2 == obj &&
|
||||
OBJ_IS_NATIVE(obj)) {
|
||||
sprop = (JSScopeProperty *) prop;
|
||||
if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)) &&
|
||||
SPROP_HAS_STUB_GETTER(sprop) &&
|
||||
SPROP_HAS_STUB_SETTER(sprop)) {
|
||||
/*
|
||||
* Fast globals use fp->vars to map the global name's
|
||||
* atomIndex to the permanent fp->varobj slot number,
|
||||
* tagged as a jsval. The atomIndex for the global's
|
||||
* name literal is identical to its fp->vars index.
|
||||
*/
|
||||
fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot);
|
||||
}
|
||||
}
|
||||
|
||||
OBJ_DROP_PROPERTY(cx, obj2, prop);
|
||||
break;
|
||||
}
|
||||
|
||||
case JSOP_DEFFUN:
|
||||
{
|
||||
jsatomid atomIndex;
|
||||
uintN flags;
|
||||
|
||||
atom = GET_ATOM(cx, script, pc);
|
||||
atomIndex = GET_ATOM_INDEX(pc);
|
||||
atom = js_GetAtom(cx, &script->atomMap, atomIndex);
|
||||
obj = ATOM_TO_OBJECT(atom);
|
||||
fun = (JSFunction *) JS_GetPrivate(cx, obj);
|
||||
id = (jsid) fun->atom;
|
||||
@ -3662,7 +4138,7 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
* as well as multiple HTML script tags.
|
||||
*/
|
||||
parent = fp->varobj;
|
||||
ok = js_CheckRedeclaration(cx, parent, id, attrs, &cond);
|
||||
ok = js_CheckRedeclaration(cx, parent, id, attrs, NULL, NULL);
|
||||
if (!ok)
|
||||
goto out;
|
||||
|
||||
@ -3675,9 +4151,20 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
? (JSPropertyOp) obj
|
||||
: NULL,
|
||||
attrs,
|
||||
NULL);
|
||||
&prop);
|
||||
if (!ok)
|
||||
goto out;
|
||||
if (attrs == (JSPROP_ENUMERATE | JSPROP_PERMANENT) &&
|
||||
script->numGlobalVars) {
|
||||
/*
|
||||
* As with JSOP_DEFVAR and JSOP_DEFCONST (above), fast globals
|
||||
* use fp->vars to map the global function name's atomIndex to
|
||||
* its permanent fp->varobj slot number, tagged as a jsval.
|
||||
*/
|
||||
sprop = (JSScopeProperty *) prop;
|
||||
fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot);
|
||||
}
|
||||
OBJ_DROP_PROPERTY(cx, parent, prop);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -3793,6 +4280,9 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
break;
|
||||
|
||||
case JSOP_CLOSURE:
|
||||
{
|
||||
jsatomid atomIndex;
|
||||
|
||||
/*
|
||||
* ECMA ed. 3 extension: a named function expression in a compound
|
||||
* statement (not at the top statement level of global code, or at
|
||||
@ -3801,7 +4291,8 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
* Get immediate operand atom, which is a function object literal.
|
||||
* From it, get the function to close.
|
||||
*/
|
||||
atom = GET_ATOM(cx, script, pc);
|
||||
atomIndex = GET_ATOM_INDEX(pc);
|
||||
atom = js_GetAtom(cx, &script->atomMap, atomIndex);
|
||||
JS_ASSERT(JSVAL_IS_FUNCTION(cx, ATOM_KEY(atom)));
|
||||
obj = ATOM_TO_OBJECT(atom);
|
||||
|
||||
@ -3830,7 +4321,8 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
attrs = fun->flags & (JSFUN_GETTER | JSFUN_SETTER);
|
||||
if (attrs)
|
||||
attrs |= JSPROP_SHARED;
|
||||
ok = OBJ_DEFINE_PROPERTY(cx, fp->varobj, (jsid)fun->atom,
|
||||
parent = fp->varobj;
|
||||
ok = OBJ_DEFINE_PROPERTY(cx, parent, (jsid)fun->atom,
|
||||
attrs ? JSVAL_VOID : OBJECT_TO_JSVAL(obj),
|
||||
(attrs & JSFUN_GETTER)
|
||||
? (JSPropertyOp) obj
|
||||
@ -3838,13 +4330,25 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
(attrs & JSFUN_SETTER)
|
||||
? (JSPropertyOp) obj
|
||||
: NULL,
|
||||
attrs | JSPROP_ENUMERATE,
|
||||
NULL);
|
||||
attrs | JSPROP_ENUMERATE
|
||||
| JSPROP_PERMANENT,
|
||||
&prop);
|
||||
if (!ok) {
|
||||
cx->newborn[GCX_OBJECT] = NULL;
|
||||
goto out;
|
||||
}
|
||||
if (attrs == 0 && script->numGlobalVars) {
|
||||
/*
|
||||
* As with JSOP_DEFVAR and JSOP_DEFCONST (above), fast globals
|
||||
* use fp->vars to map the global function name's atomIndex to
|
||||
* its permanent fp->varobj slot number, tagged as a jsval.
|
||||
*/
|
||||
sprop = (JSScopeProperty *) prop;
|
||||
fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot);
|
||||
}
|
||||
OBJ_DROP_PROPERTY(cx, parent, prop);
|
||||
break;
|
||||
}
|
||||
#endif /* JS_HAS_LEXICAL_CLOSURE */
|
||||
|
||||
#if JS_HAS_GETTER_SETTER
|
||||
@ -3927,7 +4431,7 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
attrs |= JSPROP_ENUMERATE | JSPROP_SHARED;
|
||||
|
||||
/* Check for a readonly or permanent property of the same name. */
|
||||
ok = js_CheckRedeclaration(cx, obj, id, attrs, &cond);
|
||||
ok = js_CheckRedeclaration(cx, obj, id, attrs, NULL, NULL);
|
||||
if (!ok)
|
||||
goto out;
|
||||
|
||||
|
||||
@ -247,6 +247,15 @@ js_GetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
|
||||
extern JSBool
|
||||
js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
|
||||
|
||||
#ifdef DUMP_CALL_TABLE
|
||||
# define JSOPTION_LOGCALL_TOSOURCE JS_BIT(15)
|
||||
|
||||
extern JSHashTable *js_CallTable;
|
||||
extern size_t js_LogCallToSourceLimit;
|
||||
|
||||
extern void js_DumpCallTable(JSContext *cx);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* NB: js_Invoke requires that cx is currently running JS (i.e., that cx->fp
|
||||
* is non-null).
|
||||
@ -285,7 +294,7 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
|
||||
|
||||
extern JSBool
|
||||
js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
|
||||
JSBool *foundp);
|
||||
JSObject **objp, JSProperty **propp);
|
||||
|
||||
extern JSBool
|
||||
js_Interpret(JSContext *cx, jsval *result);
|
||||
|
||||
@ -688,6 +688,45 @@ js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DUMP_CALL_TABLE
|
||||
if (cx->options & JSOPTION_LOGCALL_TOSOURCE) {
|
||||
const char *classname = OBJ_GET_CLASS(cx, obj)->name;
|
||||
size_t classnchars = strlen(classname);
|
||||
static const char classpropid[] = "C";
|
||||
const char *cp;
|
||||
size_t onchars = nchars;
|
||||
|
||||
/* 2 for ': ', 2 quotes around classname, 2 for ', ' after. */
|
||||
classnchars += sizeof classpropid - 1 + 2 + 2;
|
||||
if (ida->length)
|
||||
classnchars += 2;
|
||||
|
||||
/* 2 for the braces, 1 for the terminator */
|
||||
chars = (jschar *)
|
||||
realloc((ochars = chars),
|
||||
(nchars + classnchars + 2 + 1) * sizeof(jschar));
|
||||
if (!chars) {
|
||||
free(ochars);
|
||||
goto error;
|
||||
}
|
||||
|
||||
chars[nchars++] = '{'; /* 1 from the 2 braces */
|
||||
for (cp = classpropid; *cp; cp++)
|
||||
chars[nchars++] = (jschar) *cp;
|
||||
chars[nchars++] = ':';
|
||||
chars[nchars++] = ' '; /* 2 for ': ' */
|
||||
chars[nchars++] = '"';
|
||||
for (cp = classname; *cp; cp++)
|
||||
chars[nchars++] = (jschar) *cp;
|
||||
chars[nchars++] = '"'; /* 2 quotes */
|
||||
if (ida->length) {
|
||||
chars[nchars++] = ',';
|
||||
chars[nchars++] = ' '; /* 2 for ', ' */
|
||||
}
|
||||
|
||||
JS_ASSERT(nchars - onchars == 1 + classnchars);
|
||||
} else
|
||||
#endif
|
||||
chars[nchars++] = '{';
|
||||
|
||||
comma = NULL;
|
||||
@ -872,6 +911,10 @@ js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
|
||||
if (vsharp)
|
||||
JS_free(cx, vsharp);
|
||||
#ifdef DUMP_CALL_TABLE
|
||||
if (outermost && nchars >= js_LogCallToSourceLimit)
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -1357,7 +1400,6 @@ obj_defineGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
{
|
||||
jsval fval, junk;
|
||||
jsid id;
|
||||
JSBool found;
|
||||
uintN attrs;
|
||||
|
||||
fval = argv[1];
|
||||
@ -1370,7 +1412,7 @@ obj_defineGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
|
||||
if (!JS_ValueToId(cx, argv[0], &id))
|
||||
return JS_FALSE;
|
||||
if (!js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, &found))
|
||||
if (!js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, NULL, NULL))
|
||||
return JS_FALSE;
|
||||
/*
|
||||
* Getters and setters are just like watchpoints from an access
|
||||
@ -1389,7 +1431,6 @@ obj_defineSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
{
|
||||
jsval fval, junk;
|
||||
jsid id;
|
||||
JSBool found;
|
||||
uintN attrs;
|
||||
|
||||
fval = argv[1];
|
||||
@ -1402,7 +1443,7 @@ obj_defineSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
|
||||
if (!JS_ValueToId(cx, argv[0], &id))
|
||||
return JS_FALSE;
|
||||
if (!js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, &found))
|
||||
if (!js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, NULL, NULL))
|
||||
return JS_FALSE;
|
||||
/*
|
||||
* Getters and setters are just like watchpoints from an access
|
||||
@ -1421,16 +1462,20 @@ obj_lookupGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
{
|
||||
jsid id;
|
||||
JSObject *pobj;
|
||||
JSProperty *prop;
|
||||
JSScopeProperty *sprop;
|
||||
|
||||
if (!JS_ValueToId(cx, argv[0], &id))
|
||||
return JS_FALSE;
|
||||
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, (JSProperty **) &sprop))
|
||||
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
|
||||
return JS_FALSE;
|
||||
if (sprop) {
|
||||
if (sprop->attrs & JSPROP_GETTER)
|
||||
*rval = OBJECT_TO_JSVAL(sprop->getter);
|
||||
OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
|
||||
if (prop) {
|
||||
if (OBJ_IS_NATIVE(pobj)) {
|
||||
sprop = (JSScopeProperty *) prop;
|
||||
if (sprop->attrs & JSPROP_GETTER)
|
||||
*rval = OBJECT_TO_JSVAL(sprop->getter);
|
||||
}
|
||||
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
||||
}
|
||||
return JS_TRUE;
|
||||
}
|
||||
@ -1441,16 +1486,20 @@ obj_lookupSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
{
|
||||
jsid id;
|
||||
JSObject *pobj;
|
||||
JSProperty *prop;
|
||||
JSScopeProperty *sprop;
|
||||
|
||||
if (!JS_ValueToId(cx, argv[0], &id))
|
||||
return JS_FALSE;
|
||||
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, (JSProperty **) &sprop))
|
||||
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
|
||||
return JS_FALSE;
|
||||
if (sprop) {
|
||||
if (sprop->attrs & JSPROP_SETTER)
|
||||
*rval = OBJECT_TO_JSVAL(sprop->setter);
|
||||
OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
|
||||
if (prop) {
|
||||
if (OBJ_IS_NATIVE(pobj)) {
|
||||
sprop = (JSScopeProperty *) prop;
|
||||
if (sprop->attrs & JSPROP_SETTER)
|
||||
*rval = OBJECT_TO_JSVAL(sprop->setter);
|
||||
}
|
||||
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
||||
}
|
||||
return JS_TRUE;
|
||||
}
|
||||
@ -1558,8 +1607,10 @@ with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
|
||||
sprop->getter == js_GetLocalVariable) &&
|
||||
(sprop->attrs & JSPROP_SHARED)) {
|
||||
JS_ASSERT(OBJ_GET_CLASS(cx, proto) == &js_FunctionClass);
|
||||
for (fp = cx->fp; fp && (!fp->fun || fp->fun->native); fp = fp->down)
|
||||
for (fp = cx->fp; fp && (!fp->fun || !fp->fun->interpreted);
|
||||
fp = fp->down) {
|
||||
continue;
|
||||
}
|
||||
if (fp && fp->fun == (JSFunction *) JS_GetPrivate(cx, proto)) {
|
||||
OBJ_DROP_PROPERTY(cx, proto, *propp);
|
||||
*objp = NULL;
|
||||
@ -1813,6 +1864,7 @@ js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
|
||||
JSObject *obj, *ctor;
|
||||
JSObjectOps *ops;
|
||||
JSObjectMap *map;
|
||||
JSClass *protoclasp;
|
||||
jsval cval;
|
||||
uint32 nslots, i;
|
||||
jsval *newslots;
|
||||
@ -1837,15 +1889,20 @@ js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
|
||||
|
||||
/*
|
||||
* Share proto's map only if it has the same JSObjectOps, and only if
|
||||
* proto's class has the same private and reserved slots, as obj's map
|
||||
* and class have.
|
||||
* proto's class has the same private and reserved slots as obj's map
|
||||
* and class have. We assume that if prototype and object are of the
|
||||
* same class, they always have the same number of computed reserved
|
||||
* slots (returned via clasp->reserveSlots); otherwise, prototype and
|
||||
* object classes must have the same (null or not) reserveSlots hook.
|
||||
*/
|
||||
if (proto &&
|
||||
(map = proto->map)->ops == ops &&
|
||||
((clasp->flags ^ OBJ_GET_CLASS(cx, proto)->flags) &
|
||||
(JSCLASS_HAS_PRIVATE |
|
||||
(JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT)))
|
||||
== 0) {
|
||||
((protoclasp = OBJ_GET_CLASS(cx, proto)) == clasp ||
|
||||
(!((protoclasp->flags ^ clasp->flags) &
|
||||
(JSCLASS_HAS_PRIVATE |
|
||||
(JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT))) &&
|
||||
protoclasp->reserveSlots == clasp->reserveSlots)))
|
||||
{
|
||||
/* Default parent to the parent of the prototype's constructor. */
|
||||
if (!parent) {
|
||||
if (!OBJ_GET_PROPERTY(cx, proto,
|
||||
@ -1906,23 +1963,21 @@ bad:
|
||||
}
|
||||
|
||||
static JSBool
|
||||
FindConstructor(JSContext *cx, JSObject *scope, const char *name, jsval *vp)
|
||||
FindConstructor(JSContext *cx, JSObject *start, const char *name, jsval *vp)
|
||||
{
|
||||
JSAtom *atom;
|
||||
JSObject *obj;
|
||||
JSObject *pobj;
|
||||
JSScopeProperty *sprop;
|
||||
|
||||
atom = js_Atomize(cx, name, strlen(name), 0);
|
||||
if (!atom)
|
||||
return JS_FALSE;
|
||||
|
||||
if (scope || (cx->fp && (scope = cx->fp->scopeChain) != NULL)) {
|
||||
if (start || (cx->fp && (start = cx->fp->scopeChain) != NULL)) {
|
||||
/* Find the topmost object in the scope chain. */
|
||||
do {
|
||||
obj = scope;
|
||||
scope = OBJ_GET_PARENT(cx, obj);
|
||||
} while (scope);
|
||||
obj = start;
|
||||
start = OBJ_GET_PARENT(cx, obj);
|
||||
} while (start);
|
||||
} else {
|
||||
obj = cx->globalObject;
|
||||
if (!obj) {
|
||||
@ -1931,18 +1986,7 @@ FindConstructor(JSContext *cx, JSObject *scope, const char *name, jsval *vp)
|
||||
}
|
||||
}
|
||||
|
||||
if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, (JSProperty**)&sprop))
|
||||
return JS_FALSE;
|
||||
if (!sprop) {
|
||||
*vp = JSVAL_VOID;
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JS_ASSERT(OBJ_IS_NATIVE(pobj));
|
||||
JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)));
|
||||
*vp = OBJ_GET_SLOT(cx, pobj, sprop->slot);
|
||||
OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
|
||||
return JS_TRUE;
|
||||
return OBJ_GET_PROPERTY(cx, obj, (jsid)atom, vp);
|
||||
}
|
||||
|
||||
JSObject *
|
||||
@ -2028,27 +2072,26 @@ JSBool
|
||||
js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp)
|
||||
{
|
||||
JSObjectMap *map;
|
||||
JSClass *clasp;
|
||||
uint32 nslots, i;
|
||||
size_t nbytes;
|
||||
jsval *newslots;
|
||||
|
||||
map = obj->map;
|
||||
JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj);
|
||||
clasp = LOCKED_OBJ_GET_CLASS(obj);
|
||||
if (map->freeslot == JSSLOT_FREE(clasp)) {
|
||||
/* Adjust map->freeslot to include computed reserved slots, if any. */
|
||||
if (clasp->reserveSlots)
|
||||
map->freeslot += clasp->reserveSlots(cx, obj);
|
||||
}
|
||||
nslots = map->nslots;
|
||||
if (map->freeslot >= nslots) {
|
||||
nslots = map->freeslot;
|
||||
JS_ASSERT(nslots >= JS_INITIAL_NSLOTS);
|
||||
nslots += (nslots + 1) / 2;
|
||||
|
||||
nbytes = (nslots + 1) * sizeof(jsval);
|
||||
#if defined _MSC_VER && _MSC_VER <= 800
|
||||
if (nbytes > 60000U) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return JS_FALSE;
|
||||
}
|
||||
#endif
|
||||
|
||||
newslots = (jsval *) JS_realloc(cx, obj->slots - 1, nbytes);
|
||||
newslots = (jsval *)
|
||||
JS_realloc(cx, obj->slots - 1, (nslots + 1) * sizeof(jsval));
|
||||
if (!newslots)
|
||||
return JS_FALSE;
|
||||
for (i = 1 + newslots[0]; i <= nslots; i++)
|
||||
@ -2069,7 +2112,6 @@ js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot)
|
||||
{
|
||||
JSObjectMap *map;
|
||||
uint32 nslots;
|
||||
size_t nbytes;
|
||||
jsval *newslots;
|
||||
|
||||
OBJ_CHECK_SLOT(obj, slot);
|
||||
@ -2084,8 +2126,9 @@ js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot)
|
||||
nslots += nslots / 2;
|
||||
if (nslots < JS_INITIAL_NSLOTS)
|
||||
nslots = JS_INITIAL_NSLOTS;
|
||||
nbytes = (nslots + 1) * sizeof(jsval);
|
||||
newslots = (jsval *) JS_realloc(cx, obj->slots - 1, nbytes);
|
||||
|
||||
newslots = (jsval *)
|
||||
JS_realloc(cx, obj->slots - 1, (nslots + 1) * sizeof(jsval));
|
||||
if (!newslots)
|
||||
return;
|
||||
newslots[0] = map->nslots = nslots;
|
||||
@ -2213,6 +2256,7 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
|
||||
{
|
||||
JSClass *clasp;
|
||||
JSScope *scope;
|
||||
JSProperty *prop;
|
||||
JSScopeProperty *sprop;
|
||||
|
||||
/*
|
||||
@ -2238,8 +2282,9 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
|
||||
* finish the job via js_ChangeScopePropertyAttributes, and refresh
|
||||
* the property cache line for (obj, id) to map sprop.
|
||||
*/
|
||||
if (!js_LookupProperty(cx, obj, id, &pobj, (JSProperty **)&sprop))
|
||||
if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
|
||||
return JS_FALSE;
|
||||
sprop = (JSScopeProperty *) prop;
|
||||
if (sprop &&
|
||||
pobj == obj &&
|
||||
(sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
|
||||
@ -2258,10 +2303,10 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (sprop) {
|
||||
if (prop) {
|
||||
/* NB: call OBJ_DROP_PROPERTY, as pobj might not be native. */
|
||||
OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
|
||||
sprop = NULL;
|
||||
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
||||
prop = NULL;
|
||||
}
|
||||
}
|
||||
#endif /* JS_HAS_GETTER_SETTER */
|
||||
@ -2581,8 +2626,9 @@ JSBool
|
||||
js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
|
||||
{
|
||||
JSObject *obj2;
|
||||
JSScopeProperty *sprop;
|
||||
JSProperty *prop;
|
||||
JSScope *scope;
|
||||
JSScopeProperty *sprop;
|
||||
uint32 slot;
|
||||
|
||||
/*
|
||||
@ -2591,9 +2637,9 @@ js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
|
||||
*/
|
||||
CHECK_FOR_FUNNY_INDEX(id);
|
||||
|
||||
if (!js_LookupProperty(cx, obj, id, &obj2, (JSProperty **)&sprop))
|
||||
if (!js_LookupProperty(cx, obj, id, &obj2, &prop))
|
||||
return JS_FALSE;
|
||||
if (!sprop) {
|
||||
if (!prop) {
|
||||
jsval default_val;
|
||||
|
||||
#if JS_BUG_NULL_INDEX_PROPS
|
||||
@ -2650,12 +2696,13 @@ js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
|
||||
}
|
||||
|
||||
if (!OBJ_IS_NATIVE(obj2)) {
|
||||
OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop);
|
||||
OBJ_DROP_PROPERTY(cx, obj2, prop);
|
||||
return OBJ_GET_PROPERTY(cx, obj2, id, vp);
|
||||
}
|
||||
|
||||
/* Unlock obj2 before calling getter, relock after to avoid deadlock. */
|
||||
scope = OBJ_SCOPE(obj2);
|
||||
sprop = (JSScopeProperty *) prop;
|
||||
slot = sprop->slot;
|
||||
if (slot != SPROP_INVALID_SLOT) {
|
||||
JS_ASSERT(slot < obj2->map->freeslot);
|
||||
@ -2687,6 +2734,7 @@ JSBool
|
||||
js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
|
||||
{
|
||||
JSObject *pobj;
|
||||
JSProperty *prop;
|
||||
JSScopeProperty *sprop;
|
||||
JSScope *scope;
|
||||
uintN attrs, flags;
|
||||
@ -2702,13 +2750,14 @@ js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
|
||||
*/
|
||||
CHECK_FOR_FUNNY_INDEX(id);
|
||||
|
||||
if (!js_LookupProperty(cx, obj, id, &pobj, (JSProperty **)&sprop))
|
||||
if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
|
||||
return JS_FALSE;
|
||||
|
||||
if (sprop && !OBJ_IS_NATIVE(pobj)) {
|
||||
OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
|
||||
sprop = NULL;
|
||||
if (prop && !OBJ_IS_NATIVE(pobj)) {
|
||||
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
||||
prop = NULL;
|
||||
}
|
||||
sprop = (JSScopeProperty *) prop;
|
||||
|
||||
/*
|
||||
* Now either sprop is null, meaning id was not found in obj or one of its
|
||||
@ -2778,7 +2827,7 @@ js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
|
||||
*/
|
||||
sprop = NULL;
|
||||
}
|
||||
#ifdef __GNUC__ /* suppress bogus gcc warnings */
|
||||
#ifdef __GNUC__ /* suppress bogus gcc warnings */
|
||||
} else {
|
||||
scope = NULL;
|
||||
#endif
|
||||
@ -3182,7 +3231,11 @@ js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
|
||||
/* Object has a private scope; Enumerate all props in scope. */
|
||||
for (sprop = lastProp = SCOPE_LAST_PROP(scope); sprop;
|
||||
sprop = sprop->parent) {
|
||||
if ((sprop->attrs & JSPROP_ENUMERATE) &&
|
||||
if ((
|
||||
#ifdef DUMP_CALL_TABLE
|
||||
(cx->options & JSOPTION_LOGCALL_TOSOURCE) ||
|
||||
#endif
|
||||
(sprop->attrs & JSPROP_ENUMERATE)) &&
|
||||
!(sprop->flags & SPROP_IS_ALIAS) &&
|
||||
(!SCOPE_HAD_MIDDLE_DELETE(scope) ||
|
||||
SCOPE_HAS_PROPERTY(scope, sprop))) {
|
||||
@ -3196,7 +3249,11 @@ js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
|
||||
}
|
||||
i = length;
|
||||
for (sprop = lastProp; sprop; sprop = sprop->parent) {
|
||||
if ((sprop->attrs & JSPROP_ENUMERATE) &&
|
||||
if ((
|
||||
#ifdef DUMP_CALL_TABLE
|
||||
(cx->options & JSOPTION_LOGCALL_TOSOURCE) ||
|
||||
#endif
|
||||
(sprop->attrs & JSPROP_ENUMERATE)) &&
|
||||
!(sprop->flags & SPROP_IS_ALIAS) &&
|
||||
(!SCOPE_HAD_MIDDLE_DELETE(scope) ||
|
||||
SCOPE_HAS_PROPERTY(scope, sprop))) {
|
||||
@ -3897,6 +3954,8 @@ js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v)
|
||||
if (slot >= nslots) {
|
||||
clasp = LOCKED_OBJ_GET_CLASS(obj);
|
||||
rlimit = JSSLOT_START(clasp) + JSCLASS_RESERVED_SLOTS(clasp);
|
||||
if (clasp->reserveSlots)
|
||||
rlimit += clasp->reserveSlots(cx, obj);
|
||||
JS_ASSERT(slot < rlimit);
|
||||
if (rlimit > nslots)
|
||||
nslots = rlimit;
|
||||
|
||||
@ -64,6 +64,7 @@
|
||||
#include "jslock.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsopcode.h"
|
||||
#include "jsregexp.h"
|
||||
#include "jsscope.h"
|
||||
#include "jsscript.h"
|
||||
#include "jsstr.h"
|
||||
@ -161,7 +162,7 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc,
|
||||
return 0;
|
||||
}
|
||||
cs = &js_CodeSpec[op];
|
||||
len = (intN)cs->length;
|
||||
len = (ptrdiff_t) cs->length;
|
||||
fprintf(fp, "%05u:", loc);
|
||||
if (lines)
|
||||
fprintf(fp, "%4u", JS_PCToLineNumber(cx, script, pc));
|
||||
@ -173,7 +174,7 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc,
|
||||
op = JS_GetTrapOpcode(cx, script, pc);
|
||||
if (op == JSOP_LIMIT)
|
||||
return 0;
|
||||
len = (intN)js_CodeSpec[op].length;
|
||||
len = (ptrdiff_t) js_CodeSpec[op].length;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -229,14 +230,14 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc,
|
||||
case JOF_LOOKUPSWITCHX:
|
||||
{
|
||||
jsbytecode *pc2;
|
||||
jsint npairs;
|
||||
jsatomid npairs;
|
||||
|
||||
jmplen = (type == JOF_LOOKUPSWITCH) ? JUMP_OFFSET_LEN
|
||||
: JUMPX_OFFSET_LEN;
|
||||
pc2 = pc;
|
||||
off = GetJumpOffset(pc, pc2);
|
||||
pc2 += jmplen;
|
||||
npairs = (jsint) GET_ATOM_INDEX(pc2);
|
||||
npairs = GET_ATOM_INDEX(pc2);
|
||||
pc2 += ATOM_INDEX_LEN;
|
||||
fprintf(fp, " offset %d npairs %u", off, (uintN) npairs);
|
||||
while (npairs) {
|
||||
@ -476,8 +477,6 @@ JSPrinter *
|
||||
js_NewPrinter(JSContext *cx, const char *name, uintN indent, JSBool pretty)
|
||||
{
|
||||
JSPrinter *jp;
|
||||
JSStackFrame *fp;
|
||||
JSObjectMap *map;
|
||||
|
||||
jp = (JSPrinter *) JS_malloc(cx, sizeof(JSPrinter));
|
||||
if (!jp)
|
||||
@ -488,12 +487,6 @@ js_NewPrinter(JSContext *cx, const char *name, uintN indent, JSBool pretty)
|
||||
jp->pretty = pretty;
|
||||
jp->script = NULL;
|
||||
jp->scope = NULL;
|
||||
fp = cx->fp;
|
||||
if (fp && fp->fun && fp->fun->object) {
|
||||
map = fp->fun->object->map;
|
||||
if (MAP_IS_NATIVE(map))
|
||||
jp->scope = (JSScope *)map;
|
||||
}
|
||||
return jp;
|
||||
}
|
||||
|
||||
@ -837,7 +830,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
JSContext *cx;
|
||||
JSPrinter *jp, *jp2;
|
||||
jsbytecode *endpc, *done, *forelem_tail, *forelem_done;
|
||||
ptrdiff_t len, todo, oplen, cond, next, tail;
|
||||
ptrdiff_t tail, todo, len, oplen, cond, next;
|
||||
JSOp op, lastop, saveop;
|
||||
const JSCodeSpec *cs, *topcs;
|
||||
jssrcnote *sn, *sn2;
|
||||
@ -889,8 +882,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
jp = ss->printer;
|
||||
endpc = pc + nb;
|
||||
forelem_tail = forelem_done = NULL;
|
||||
todo = -2; /* NB: different from Sprint() error return. */
|
||||
tail = -1;
|
||||
todo = -2; /* NB: different from Sprint() error return. */
|
||||
op = JSOP_NOP;
|
||||
sn = NULL;
|
||||
rval = NULL;
|
||||
@ -1579,6 +1572,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
|
||||
case JSOP_SETCONST:
|
||||
case JSOP_SETNAME:
|
||||
case JSOP_SETGVAR:
|
||||
atom = GET_ATOM(cx, jp->script, pc);
|
||||
do_setname:
|
||||
lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
|
||||
@ -1712,6 +1706,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
|
||||
case JSOP_INCNAME:
|
||||
case JSOP_DECNAME:
|
||||
case JSOP_INCGVAR:
|
||||
case JSOP_DECGVAR:
|
||||
atom = GET_ATOM(cx, jp->script, pc);
|
||||
do_incatom:
|
||||
lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
|
||||
@ -1754,6 +1750,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
|
||||
case JSOP_NAMEINC:
|
||||
case JSOP_NAMEDEC:
|
||||
case JSOP_GVARINC:
|
||||
case JSOP_GVARDEC:
|
||||
atom = GET_ATOM(cx, jp->script, pc);
|
||||
do_atominc:
|
||||
lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
|
||||
@ -1860,6 +1858,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
goto do_name;
|
||||
|
||||
case JSOP_NAME:
|
||||
case JSOP_GETGVAR:
|
||||
atom = GET_ATOM(cx, jp->script, pc);
|
||||
do_name:
|
||||
sn = js_GetSrcNote(jp->script, pc);
|
||||
@ -1903,21 +1902,23 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
break;
|
||||
|
||||
case JSOP_OBJECT:
|
||||
case JSOP_REGEXP:
|
||||
case JSOP_ANONFUNOBJ:
|
||||
case JSOP_NAMEDFUNOBJ:
|
||||
atom = GET_ATOM(cx, jp->script, pc);
|
||||
if (op == JSOP_OBJECT) {
|
||||
str = js_ValueToSource(cx, ATOM_KEY(atom));
|
||||
if (!str)
|
||||
if (op == JSOP_OBJECT || op == JSOP_REGEXP) {
|
||||
if (!js_regexp_toString(cx, ATOM_TO_OBJECT(atom), 0, NULL,
|
||||
&val)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
} else {
|
||||
if (!js_fun_toString(cx, ATOM_TO_OBJECT(atom),
|
||||
JS_DONT_PRETTY_PRINT, 0, NULL,
|
||||
&val)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
str = JSVAL_TO_STRING(val);
|
||||
}
|
||||
str = JSVAL_TO_STRING(val);
|
||||
todo = SprintPut(&ss->sprinter, JS_GetStringBytes(str),
|
||||
JSSTRING_LENGTH(str));
|
||||
break;
|
||||
@ -1990,7 +1991,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
{
|
||||
jsbytecode *pc2;
|
||||
ptrdiff_t jmplen, off, off2;
|
||||
jsint npairs;
|
||||
jsatomid npairs, k;
|
||||
TableEntry *table;
|
||||
|
||||
sn = js_GetSrcNote(jp->script, pc);
|
||||
@ -2001,29 +2002,29 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
pc2 = pc;
|
||||
off = GetJumpOffset(pc, pc2);
|
||||
pc2 += jmplen;
|
||||
npairs = (jsint) GET_ATOM_INDEX(pc2);
|
||||
npairs = GET_ATOM_INDEX(pc2);
|
||||
pc2 += ATOM_INDEX_LEN;
|
||||
|
||||
table = (TableEntry *)
|
||||
JS_malloc(cx, (size_t)npairs * sizeof *table);
|
||||
if (!table)
|
||||
return JS_FALSE;
|
||||
for (i = 0; i < npairs; i++) {
|
||||
for (k = 0; k < npairs; k++) {
|
||||
sn = js_GetSrcNote(jp->script, pc2);
|
||||
if (sn) {
|
||||
JS_ASSERT(SN_TYPE(sn) == SRC_LABEL);
|
||||
table[i].label =
|
||||
table[k].label =
|
||||
js_GetAtom(cx, &jp->script->atomMap, (jsatomid)
|
||||
js_GetSrcNoteOffset(sn, 0));
|
||||
} else {
|
||||
table[i].label = NULL;
|
||||
table[k].label = NULL;
|
||||
}
|
||||
atom = GET_ATOM(cx, jp->script, pc2);
|
||||
pc2 += ATOM_INDEX_LEN;
|
||||
off2 = GetJumpOffset(pc, pc2);
|
||||
pc2 += jmplen;
|
||||
table[i].key = ATOM_KEY(atom);
|
||||
table[i].offset = off2;
|
||||
table[k].key = ATOM_KEY(atom);
|
||||
table[k].offset = off2;
|
||||
}
|
||||
|
||||
ok = DecompileSwitch(ss, table, (uintN)npairs, pc, len, off,
|
||||
@ -2396,11 +2397,11 @@ js_DecompileFunctionBody(JSPrinter *jp, JSFunction *fun)
|
||||
JSScope *scope, *save;
|
||||
JSBool ok;
|
||||
|
||||
script = fun->script;
|
||||
if (!script) {
|
||||
if (!fun->interpreted) {
|
||||
js_printf(jp, native_code_str);
|
||||
return JS_TRUE;
|
||||
}
|
||||
script = fun->u.script;
|
||||
scope = fun->object ? OBJ_SCOPE(fun->object) : NULL;
|
||||
save = jp->scope;
|
||||
jp->scope = scope;
|
||||
@ -2442,7 +2443,7 @@ js_DecompileFunction(JSPrinter *jp, JSFunction *fun)
|
||||
return JS_FALSE;
|
||||
js_puts(jp, "(");
|
||||
|
||||
if (fun->script && fun->object) {
|
||||
if (fun->interpreted && fun->object) {
|
||||
/*
|
||||
* Print the parameters.
|
||||
*
|
||||
@ -2487,10 +2488,10 @@ js_DecompileFunction(JSPrinter *jp, JSFunction *fun)
|
||||
js_printf(jp, ") {\n");
|
||||
indent = jp->indent;
|
||||
jp->indent += 4;
|
||||
if (fun->script && fun->object) {
|
||||
if (fun->interpreted && fun->object) {
|
||||
oldscope = jp->scope;
|
||||
jp->scope = scope;
|
||||
ok = js_DecompileScript(jp, fun->script);
|
||||
ok = js_DecompileScript(jp, fun->u.script);
|
||||
jp->scope = oldscope;
|
||||
if (!ok) {
|
||||
jp->indent = indent;
|
||||
@ -2541,7 +2542,7 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
|
||||
* in which a decompilable bytecode string that generated the
|
||||
* value as an actual argument might exist.
|
||||
*/
|
||||
JS_ASSERT(!fp->script && (!fp->fun || fp->fun->native));
|
||||
JS_ASSERT(!fp->script && !(fp->fun && fp->fun->interpreted));
|
||||
down = fp->down;
|
||||
if (!down)
|
||||
goto do_fallback;
|
||||
@ -2693,11 +2694,16 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
|
||||
tmp = NULL;
|
||||
}
|
||||
|
||||
name = NULL;
|
||||
jp = js_NewPrinter(cx, "js_DecompileValueGenerator", 0, JS_FALSE);
|
||||
if (jp && js_DecompileCode(jp, script, begin, len))
|
||||
name = js_GetPrinterOutput(jp);
|
||||
else
|
||||
name = NULL;
|
||||
if (jp) {
|
||||
if (fp->fun && fp->fun->object) {
|
||||
JS_ASSERT(OBJ_IS_NATIVE(fp->fun->object));
|
||||
jp->scope = OBJ_SCOPE(fp->fun->object);
|
||||
}
|
||||
if (js_DecompileCode(jp, script, begin, len))
|
||||
name = js_GetPrinterOutput(jp);
|
||||
}
|
||||
js_DestroyPrinter(jp);
|
||||
if (tmp)
|
||||
JS_free(cx, tmp);
|
||||
|
||||
@ -331,3 +331,14 @@ OPDEF(JSOP_BACKPATCH_PUSH,151,"backpatch_push",NULL, 3, 0, 1, 0, JOF_JUMP|J
|
||||
/* Set and get return value pseudo-register in stack frame. */
|
||||
OPDEF(JSOP_SETRVAL, 152,"setrval", NULL, 1, 1, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_RETRVAL, 153,"retrval", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
|
||||
/* Optimized global variable ops (we don't bother doing a JSOP_FORGVAR op). */
|
||||
OPDEF(JSOP_GETGVAR, 154,"getgvar", NULL, 3, 0, 1, 12, JOF_CONST|JOF_NAME)
|
||||
OPDEF(JSOP_SETGVAR, 155,"setgvar", NULL, 3, 1, 1, 1, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING)
|
||||
OPDEF(JSOP_INCGVAR, 156,"incgvar", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_INC)
|
||||
OPDEF(JSOP_DECGVAR, 157,"decgvar", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_DEC)
|
||||
OPDEF(JSOP_GVARINC, 158,"gvarinc", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_INC|JOF_POST)
|
||||
OPDEF(JSOP_GVARDEC, 159,"gvardec", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_DEC|JOF_POST)
|
||||
|
||||
/* Regular expression literal requiring special fork-on-exec handling. */
|
||||
OPDEF(JSOP_REGEXP, 160,"regexp", NULL, 3, 0, 1, 12, JOF_CONST)
|
||||
|
||||
@ -623,6 +623,7 @@ FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun,
|
||||
frame.fun = fun;
|
||||
frame.varobj = frame.scopeChain = funobj;
|
||||
frame.down = fp;
|
||||
frame.flags = (fp->flags & JSFRAME_COMPILE_N_GO);
|
||||
cx->fp = &frame;
|
||||
}
|
||||
|
||||
@ -691,10 +692,11 @@ js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun)
|
||||
* No need to emit code here -- Statements (via FunctionBody) already
|
||||
* has. See similar comment in js_CompileTokenStream, and bug 108257.
|
||||
*/
|
||||
fun->script = js_NewScriptFromCG(cx, &funcg, fun);
|
||||
if (!fun->script) {
|
||||
fun->u.script = js_NewScriptFromCG(cx, &funcg, fun);
|
||||
if (!fun->u.script) {
|
||||
ok = JS_FALSE;
|
||||
} else {
|
||||
fun->interpreted = JS_TRUE;
|
||||
if (funcg.treeContext.flags & TCF_FUN_HEAVYWEIGHT)
|
||||
fun->flags |= JSFUN_HEAVYWEIGHT;
|
||||
ok = JS_TRUE;
|
||||
@ -2796,7 +2798,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
again:
|
||||
/*
|
||||
* Control flows here after #n= is scanned. If the following primary is
|
||||
* not valid after such a "sharp variable" definition, the token type case
|
||||
* not valid after such a "sharp variable" definition, the tt switch case
|
||||
* should set notsharp.
|
||||
*/
|
||||
#endif
|
||||
@ -3057,6 +3059,26 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
if (pn->pn_atom == cx->runtime->atomState.parentAtom ||
|
||||
pn->pn_atom == cx->runtime->atomState.protoAtom) {
|
||||
tc->flags |= TCF_FUN_HEAVYWEIGHT;
|
||||
} else {
|
||||
JSAtomListElement *ale;
|
||||
JSStackFrame *fp;
|
||||
JSStmtInfo *stmt;
|
||||
|
||||
/* Measure optimizable global variable uses. */
|
||||
ATOM_LIST_SEARCH(ale, &tc->decls, pn->pn_atom);
|
||||
if (ale &&
|
||||
!(fp = cx->fp)->fun &&
|
||||
fp->scopeChain == fp->varobj &&
|
||||
!js_InWithStatement(tc) &&
|
||||
!js_InCatchBlock(tc, pn->pn_atom)) {
|
||||
tc->globalUses++;
|
||||
for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
|
||||
if (STMT_IS_LOOP(stmt)) {
|
||||
tc->loopyGlobalUses++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@ -174,7 +174,8 @@ JS_BEGIN_EXTERN_C
|
||||
* TOK_USESHARP nullary pn_num: jsint value of n in #n#
|
||||
* TOK_RP unary pn_kid: parenthesized expression
|
||||
* TOK_NAME, name pn_atom: name, string, or object atom
|
||||
* TOK_STRING, pn_op: JSOP_NAME, JSOP_STRING, or JSOP_OBJECT
|
||||
* TOK_STRING, pn_op: JSOP_NAME, JSOP_STRING, or JSOP_OBJECT, or
|
||||
* JSOP_REGEXP
|
||||
* TOK_OBJECT If JSOP_NAME, pn_op may be JSOP_*ARG or JSOP_*VAR
|
||||
* with pn_slot >= 0 and pn_attrs telling const-ness
|
||||
* TOK_NUMBER dval pn_dval: double value of numeric literal
|
||||
|
||||
@ -326,6 +326,21 @@ typedef JSBool
|
||||
typedef uint32
|
||||
(* JS_DLL_CALLBACK JSMarkOp)(JSContext *cx, JSObject *obj, void *arg);
|
||||
|
||||
/*
|
||||
* The optional JSClass.reserveSlots hook allows a class to make computed
|
||||
* per-instance object slots reservations, in addition to or instead of using
|
||||
* JSCLASS_HAS_RESERVED_SLOTS(n) in the JSClass.flags initializer to reserve
|
||||
* a constant-per-class number of slots. Implementations of this hook should
|
||||
* return the number of slots to reserve, not including any reserved by using
|
||||
* JSCLASS_HAS_RESERVED_SLOTS(n) in JSClass.flags.
|
||||
*
|
||||
* NB: called with obj locked by the JSObjectOps-specific mutual exclusion
|
||||
* mechanism appropriate for obj, so don't nest other operations that might
|
||||
* also lock obj.
|
||||
*/
|
||||
typedef uint32
|
||||
(* JS_DLL_CALLBACK JSReserveSlotsOp)(JSContext *cx, JSObject *obj);
|
||||
|
||||
/* JSObjectOps function pointer typedefs. */
|
||||
|
||||
/*
|
||||
|
||||
@ -175,11 +175,11 @@ typedef struct CompilerState {
|
||||
const jschar *cpbegin;
|
||||
const jschar *cpend;
|
||||
const jschar *cp;
|
||||
uintN flags;
|
||||
uint16 flags;
|
||||
uint16 parenCount;
|
||||
uint16 classCount; /* number of [] encountered */
|
||||
uint16 treeDepth; /* maximum depth of parse tree */
|
||||
size_t progLength; /* estimated bytecode length */
|
||||
uintN treeDepth; /* maximum depth of parse tree */
|
||||
RENode *result;
|
||||
struct {
|
||||
const jschar *start; /* small cache of class strings */
|
||||
@ -1533,7 +1533,7 @@ js_NewRegExp(JSContext *cx, JSTokenStream *ts,
|
||||
CompilerState state;
|
||||
size_t resize;
|
||||
jsbytecode *endPC;
|
||||
uint32 i;
|
||||
uintN i;
|
||||
size_t len;
|
||||
|
||||
re = NULL;
|
||||
@ -1568,27 +1568,31 @@ js_NewRegExp(JSContext *cx, JSTokenStream *ts,
|
||||
if (!re)
|
||||
goto out;
|
||||
|
||||
re->nrefs = 1;
|
||||
re->classCount = state.classCount;
|
||||
if (state.classCount) {
|
||||
re->classList = (RECharSet *)JS_malloc(cx,
|
||||
sizeof(RECharSet) *
|
||||
state.classCount);
|
||||
if (!re->classList)
|
||||
if (re->classCount) {
|
||||
re->classList = (RECharSet *)
|
||||
JS_malloc(cx, re->classCount * sizeof(RECharSet));
|
||||
if (!re->classList) {
|
||||
js_DestroyRegExp(cx, re);
|
||||
re = NULL;
|
||||
goto out;
|
||||
}
|
||||
else
|
||||
}
|
||||
} else {
|
||||
re->classList = NULL;
|
||||
}
|
||||
endPC = EmitREBytecode(&state, re, state.treeDepth, re->program, state.result);
|
||||
if (!endPC) {
|
||||
js_DestroyRegExp(cx, re);
|
||||
re = NULL;
|
||||
goto out;
|
||||
}
|
||||
*endPC++ = REOP_END;
|
||||
JS_ASSERT(endPC <= (re->program + (state.progLength + 1)));
|
||||
|
||||
re->nrefs = 1;
|
||||
re->parenCount = state.parenCount;
|
||||
re->flags = flags;
|
||||
re->cloneIndex = 0;
|
||||
re->parenCount = state.parenCount;
|
||||
re->source = str;
|
||||
|
||||
out:
|
||||
@ -2013,9 +2017,9 @@ ProcessCharSet(REGlobalData *gData, RECharSet *charSet)
|
||||
void
|
||||
js_DestroyRegExp(JSContext *cx, JSRegExp *re)
|
||||
{
|
||||
uintN i;
|
||||
if (JS_ATOMIC_DECREMENT(&re->nrefs) == 0) {
|
||||
if (re->classList) {
|
||||
uintN i;
|
||||
for (i = 0; i < re->classCount; i++) {
|
||||
if (re->classList[i].converted)
|
||||
JS_free(cx, re->classList[i].u.bits);
|
||||
@ -3340,9 +3344,9 @@ JSClass js_RegExpClass = {
|
||||
|
||||
static const jschar empty_regexp_ucstr[] = {'(', '?', ':', ')', 0};
|
||||
|
||||
static JSBool
|
||||
regexp_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
jsval *rval)
|
||||
JSBool
|
||||
js_regexp_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
jsval *rval)
|
||||
{
|
||||
JSRegExp *re;
|
||||
const jschar *source;
|
||||
@ -3558,9 +3562,9 @@ regexp_test(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
|
||||
static JSFunctionSpec regexp_methods[] = {
|
||||
#if JS_HAS_TOSOURCE
|
||||
{js_toSource_str, regexp_toString, 0,0,0},
|
||||
{js_toSource_str, js_regexp_toString, 0,0,0},
|
||||
#endif
|
||||
{js_toString_str, regexp_toString, 0,0,0},
|
||||
{js_toString_str, js_regexp_toString, 0,0,0},
|
||||
{"compile", regexp_compile, 1,0,0},
|
||||
{"exec", regexp_exec, 0,0,0},
|
||||
{"test", regexp_test, 0,0,0},
|
||||
|
||||
@ -53,8 +53,8 @@
|
||||
struct JSRegExpStatics {
|
||||
JSString *input; /* input string to match (perl $_, GC root) */
|
||||
JSBool multiline; /* whether input contains newlines (perl $*) */
|
||||
uintN parenCount; /* number of valid elements in parens[] */
|
||||
uintN moreLength; /* number of allocated elements in moreParens */
|
||||
uint16 parenCount; /* number of valid elements in parens[] */
|
||||
uint16 moreLength; /* number of allocated elements in moreParens */
|
||||
JSSubString parens[9]; /* last set of parens matched (perl $1, $2) */
|
||||
JSSubString *moreParens; /* null or realloc'd vector for $10, etc. */
|
||||
JSSubString lastMatch; /* last string matched (perl $&) */
|
||||
@ -72,14 +72,14 @@ struct JSRegExpStatics {
|
||||
*
|
||||
*/
|
||||
typedef struct RECharSet {
|
||||
JSBool converted;
|
||||
JSBool sense;
|
||||
uint16 length;
|
||||
JSPackedBool converted;
|
||||
JSPackedBool sense;
|
||||
uint16 length;
|
||||
union {
|
||||
uint8 *bits;
|
||||
uint8 *bits;
|
||||
struct {
|
||||
uint16 startIndex;
|
||||
uint16 length;
|
||||
uint16 startIndex;
|
||||
uint16 length;
|
||||
} src;
|
||||
} u;
|
||||
} RECharSet;
|
||||
@ -99,12 +99,14 @@ typedef struct RENode RENode;
|
||||
|
||||
struct JSRegExp {
|
||||
jsrefcount nrefs; /* reference count */
|
||||
uint32 parenCount:24, /* number of parenthesized submatches */
|
||||
flags:8; /* flags, see jsapi.h's JSREG_* defines */
|
||||
uint32 classCount; /* count [...] bitmaps */
|
||||
uint16 flags; /* flags, see jsapi.h's JSREG_* defines */
|
||||
uint16 cloneIndex; /* index in fp->vars or funobj->slots of
|
||||
cloned regexp object */
|
||||
uint16 parenCount; /* number of parenthesized submatches */
|
||||
uint16 classCount; /* count [...] bitmaps */
|
||||
RECharSet *classList; /* list of [...] bitmaps */
|
||||
JSString *source; /* locked source string, sans // */
|
||||
jsbytecode program[1]; /* regular expression bytecode */
|
||||
jsbytecode program[1]; /* regular expression bytecode */
|
||||
};
|
||||
|
||||
extern JSRegExp *
|
||||
@ -147,7 +149,14 @@ extern JSObject *
|
||||
js_InitRegExpClass(JSContext *cx, JSObject *obj);
|
||||
|
||||
/*
|
||||
* Create a new RegExp object.
|
||||
* Export js_regexp_toString to the decompiler.
|
||||
*/
|
||||
extern JSBool
|
||||
js_regexp_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
jsval *rval);
|
||||
|
||||
/*
|
||||
* Create, serialize/deserialize, or clone a RegExp object.
|
||||
*/
|
||||
extern JSObject *
|
||||
js_NewRegExpObject(JSContext *cx, JSTokenStream *ts,
|
||||
@ -159,6 +168,9 @@ js_XDRRegExp(JSXDRState *xdr, JSObject **objp);
|
||||
extern JSObject *
|
||||
js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *parent);
|
||||
|
||||
/*
|
||||
* Get and set the per-object (clone or clone-parent) lastIndex slot.
|
||||
*/
|
||||
extern JSBool
|
||||
js_GetLastIndex(JSContext *cx, JSObject *obj, jsdouble *lastIndex);
|
||||
|
||||
|
||||
@ -1196,7 +1196,17 @@ skipline:
|
||||
atom = js_AtomizeObject(cx, obj, 0);
|
||||
if (!atom)
|
||||
RETURN(TOK_ERROR);
|
||||
tp->t_op = JSOP_OBJECT;
|
||||
|
||||
/*
|
||||
* If the regexp's script is one-shot, we can avoid the extra
|
||||
* fork-on-exec costs of JSOP_REGEXP by selecting JSOP_OBJECT.
|
||||
* Otherwise, to avoid incorrect proto, parent, and lastIndex
|
||||
* sharing among threads and sequentially across re-execution,
|
||||
* select JSOP_REGEXP.
|
||||
*/
|
||||
tp->t_op = (cx->fp->flags & (JSFRAME_EVAL | JSFRAME_COMPILE_N_GO))
|
||||
? JSOP_OBJECT
|
||||
: JSOP_REGEXP;
|
||||
tp->t_atom = atom;
|
||||
RETURN(TOK_OBJECT);
|
||||
}
|
||||
|
||||
@ -196,7 +196,7 @@ js_DestroyScope(JSContext *cx, JSScope *scope)
|
||||
JS_free(cx, scope);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_brendan
|
||||
#ifdef DUMP_SCOPE_STATS
|
||||
typedef struct JSScopeStats {
|
||||
jsrefcount searches;
|
||||
jsrefcount steps;
|
||||
@ -1277,7 +1277,7 @@ js_ChangeScopePropertyAttrs(JSContext *cx, JSScope *scope,
|
||||
child.attrs, child.flags, child.shortid);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_brendan
|
||||
#ifdef DUMP_SCOPE_STATS
|
||||
if (!newsprop)
|
||||
METER(changeFailures);
|
||||
#endif
|
||||
@ -1372,7 +1372,7 @@ js_ClearScope(JSContext *cx, JSScope *scope)
|
||||
InitMinimalScope(scope);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_brendan
|
||||
#ifdef DUMP_SCOPE_STATS
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
@ -1433,7 +1433,47 @@ js_MeterPropertyTree(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number,
|
||||
return JS_DHASH_NEXT;
|
||||
}
|
||||
|
||||
#endif /* DEBUG_brendan */
|
||||
#include "jsprf.h"
|
||||
|
||||
static void
|
||||
DumpSubtree(JSScopeProperty *sprop, int level, FILE *fp)
|
||||
{
|
||||
char buf[10];
|
||||
JSScopeProperty *kids, *kid;
|
||||
PropTreeKidsChunk *chunk;
|
||||
uintN i;
|
||||
|
||||
fprintf(fp, "%*sid %s g/s %p/%p slot %lu attrs %x flags %x shortid %d\n",
|
||||
level, "",
|
||||
JSVAL_IS_INT(sprop->id)
|
||||
? (JS_snprintf(buf, sizeof buf, "%ld", JSVAL_TO_INT(sprop->id)),
|
||||
buf)
|
||||
: JS_GetStringBytes(ATOM_TO_STRING((JSAtom *) sprop->id)),
|
||||
(void *) sprop->getter, (void *) sprop->setter,
|
||||
(unsigned long) sprop->slot, sprop->attrs, sprop->flags,
|
||||
sprop->shortid);
|
||||
kids = sprop->kids;
|
||||
if (kids) {
|
||||
++level;
|
||||
if (KIDS_IS_CHUNKY(kids)) {
|
||||
chunk = KIDS_TO_CHUNK(kids);
|
||||
do {
|
||||
for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) {
|
||||
kid = chunk->kids[i];
|
||||
if (!kid)
|
||||
break;
|
||||
JS_ASSERT(kid->parent == sprop);
|
||||
DumpSubtree(kid, level, fp);
|
||||
}
|
||||
} while ((chunk = chunk->next) != NULL);
|
||||
} else {
|
||||
kid = kids;
|
||||
DumpSubtree(kid, level, fp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* DUMP_SCOPE_STATS */
|
||||
|
||||
void
|
||||
js_SweepScopeProperties(JSRuntime *rt)
|
||||
@ -1444,7 +1484,7 @@ js_SweepScopeProperties(JSRuntime *rt)
|
||||
PropTreeKidsChunk *chunk, *nextChunk;
|
||||
uintN i;
|
||||
|
||||
#ifdef DEBUG_brendan
|
||||
#ifdef DUMP_SCOPE_STATS
|
||||
uint32 livePropCapacity = 0, totalLiveCount = 0;
|
||||
static FILE *logfp;
|
||||
if (!logfp)
|
||||
@ -1454,7 +1494,7 @@ js_SweepScopeProperties(JSRuntime *rt)
|
||||
JS_DHashTableEnumerate(&rt->propertyTreeHash, js_MeterPropertyTree, NULL);
|
||||
|
||||
{
|
||||
double mean = 0., var = 0., sigma = 0.;
|
||||
double mean = 0.0, var = 0.0, sigma = 0.0;
|
||||
double nodesum = rt->livePropTreeNodes;
|
||||
double kidsum = js_nkids_sum;
|
||||
if (nodesum > 0 && kidsum >= 0) {
|
||||
@ -1466,7 +1506,7 @@ js_SweepScopeProperties(JSRuntime *rt)
|
||||
var /= nodesum * (nodesum - 1);
|
||||
|
||||
/* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */
|
||||
sigma = (var != 0.) ? sqrt(var) : 0.;
|
||||
sigma = (var != 0.0) ? sqrt(var) : 0.0;
|
||||
}
|
||||
|
||||
fprintf(logfp,
|
||||
@ -1542,7 +1582,7 @@ js_SweepScopeProperties(JSRuntime *rt)
|
||||
FREENODE_REMOVE(sprop);
|
||||
JS_ARENA_DESTROY(&rt->propertyArenaPool, a, ap);
|
||||
} else {
|
||||
#ifdef DEBUG_brendan
|
||||
#ifdef DUMP_SCOPE_STATS
|
||||
livePropCapacity += limit - (JSScopeProperty *) a->base;
|
||||
totalLiveCount += liveCount;
|
||||
#endif
|
||||
@ -1550,11 +1590,29 @@ js_SweepScopeProperties(JSRuntime *rt)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_brendan
|
||||
#ifdef DUMP_SCOPE_STATS
|
||||
fprintf(logfp, " arenautil %g%%\n",
|
||||
(totalLiveCount * 100.) / livePropCapacity);
|
||||
(totalLiveCount * 100.0) / livePropCapacity);
|
||||
fflush(logfp);
|
||||
#endif
|
||||
|
||||
#ifdef DUMP_PROPERTY_TREE
|
||||
{
|
||||
FILE *dumpfp = fopen("/tmp/proptree.dump", "w");
|
||||
if (dumpfp) {
|
||||
JSPropertyTreeEntry *pte, *end;
|
||||
|
||||
pte = (JSPropertyTreeEntry *) rt->propertyTreeHash.entryStore;
|
||||
end = pte + JS_DHASH_TABLE_SIZE(&rt->propertyTreeHash);
|
||||
while (pte < end) {
|
||||
if (pte->child)
|
||||
DumpSubtree(pte->child, 0, dumpfp);
|
||||
pte++;
|
||||
}
|
||||
fclose(dumpfp);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
JSBool
|
||||
|
||||
@ -304,6 +304,9 @@ struct JSScopeProperty {
|
||||
#define SPROP_HAS_VALID_SLOT(sprop, scope) \
|
||||
((sprop)->slot < (scope)->map.freeslot)
|
||||
|
||||
#define SPROP_HAS_STUB_GETTER(sprop) (!(sprop)->getter)
|
||||
#define SPROP_HAS_STUB_SETTER(sprop) (!(sprop)->setter)
|
||||
|
||||
#define SPROP_CALL_GETTER(cx,sprop,getter,obj,obj2,vp) \
|
||||
(!(getter) || \
|
||||
(getter)(cx, OBJ_THIS_OBJECT(cx,obj), SPROP_USERID(sprop), vp))
|
||||
|
||||
@ -419,7 +419,8 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
|
||||
if (xdr->mode == JSXDR_ENCODE) {
|
||||
length = script->length;
|
||||
prologLength = PTRDIFF(script->main, script->code, jsbytecode);
|
||||
version = (int32)script->version;
|
||||
JS_ASSERT((int16)script->version != JSVERSION_UNKNOWN);
|
||||
version = (uint32)script->version | (script->numGlobalVars << 16);
|
||||
lineno = (uint32)script->lineno;
|
||||
depth = (uint32)script->depth;
|
||||
|
||||
@ -461,7 +462,8 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
|
||||
return JS_FALSE;
|
||||
if (magic >= JSXDR_MAGIC_SCRIPT_2) {
|
||||
script->main += prologLength;
|
||||
script->version = (JSVersion) version;
|
||||
script->version = (JSVersion) (version & 0xffff);
|
||||
script->numGlobalVars = (uint16) (version >> 16);
|
||||
|
||||
/* If we know nsrcnotes, we allocated space for notes in script. */
|
||||
if (magic >= JSXDR_MAGIC_SCRIPT_4)
|
||||
@ -473,7 +475,7 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
|
||||
/*
|
||||
* Control hereafter must goto error on failure, in order for the DECODE
|
||||
* case to destroy script and conditionally free notes, which if non-null
|
||||
* in the (DECODE and version < _4) case must point at a temporary vector
|
||||
* in the (DECODE and magic < _4) case must point at a temporary vector
|
||||
* allocated just below.
|
||||
*/
|
||||
if (!JS_XDRBytes(xdr, (char *)script->code, length * sizeof(jsbytecode)) ||
|
||||
@ -1085,6 +1087,7 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun)
|
||||
script->main += prologLength;
|
||||
memcpy(script->code, CG_PROLOG_BASE(cg), prologLength * sizeof(jsbytecode));
|
||||
memcpy(script->main, CG_BASE(cg), mainLength * sizeof(jsbytecode));
|
||||
script->numGlobalVars = cg->treeContext.numGlobalVars;
|
||||
if (!js_InitAtomMap(cx, &script->atomMap, &cg->atomList))
|
||||
goto bad;
|
||||
|
||||
@ -1216,7 +1219,8 @@ js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
|
||||
if (*pc == JSOP_DEFFUN) {
|
||||
atom = GET_ATOM(cx, script, pc);
|
||||
fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(atom));
|
||||
return fun->script->lineno;
|
||||
JS_ASSERT(fun->interpreted);
|
||||
return fun->u.script->lineno;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@ -67,7 +67,8 @@ struct JSScript {
|
||||
jsbytecode *code; /* bytecodes and their immediate operands */
|
||||
uint32 length; /* length of code vector */
|
||||
jsbytecode *main; /* main entry point, after predef'ing prolog */
|
||||
JSVersion version; /* JS version under which script was compiled */
|
||||
uint16 version; /* JS version under which script was compiled */
|
||||
uint16 numGlobalVars; /* declared global var/const/function count */
|
||||
JSAtomMap atomMap; /* maps immediate index to literal struct */
|
||||
const char *filename; /* source filename or null */
|
||||
uintN lineno; /* base line number of script */
|
||||
|
||||
@ -48,6 +48,7 @@
|
||||
#include "jsprf.h"
|
||||
#include "jsapi.h"
|
||||
#include "jscntxt.h"
|
||||
#include "jsnum.h"
|
||||
#include "jsobj.h" /* js_XDRObject */
|
||||
#include "jsscript.h" /* js_XDRScript */
|
||||
#include "jsstr.h"
|
||||
@ -475,19 +476,14 @@ JS_XDRStringOrNull(JSXDRState *xdr, JSString **strp)
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_XDRDouble(JSXDRState *xdr, jsdouble **dp)
|
||||
{
|
||||
jsdouble d;
|
||||
jsdpun u;
|
||||
|
||||
if (xdr->mode == JSXDR_ENCODE)
|
||||
d = **dp;
|
||||
#if IS_BIG_ENDIAN
|
||||
if (!JS_XDRUint32(xdr, (uint32 *)&d + 1) ||
|
||||
!JS_XDRUint32(xdr, (uint32 *)&d))
|
||||
#else
|
||||
if (!JS_XDRUint32(xdr, (uint32 *)&d) ||
|
||||
!JS_XDRUint32(xdr, (uint32 *)&d + 1))
|
||||
#endif
|
||||
u.d = **dp;
|
||||
if (!JS_XDRUint32(xdr, &u.s.lo) || !JS_XDRUint32(xdr, &u.s.hi))
|
||||
return JS_FALSE;
|
||||
if (xdr->mode == JSXDR_DECODE) {
|
||||
*dp = JS_NewDouble(xdr->cx, d);
|
||||
*dp = JS_NewDouble(xdr->cx, u.d);
|
||||
if (!*dp)
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user