- 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:
brendan%mozilla.org 2004-04-13 01:25:17 +00:00
parent cc4c57fe36
commit 25a4d6d341
30 changed files with 1424 additions and 508 deletions

View File

@ -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;
}

View File

@ -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")

View File

@ -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;

View File

@ -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. */

View File

@ -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;
}

View File

@ -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)

View File

@ -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);

View File

@ -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 {

View File

@ -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)

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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.

View File

@ -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 */

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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;

View File

@ -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

View File

@ -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. */
/*

View File

@ -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},

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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))

View File

@ -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;
}
/*

View File

@ -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 */

View File

@ -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;
}