Fix for embeddings that precompile and execute using different globals/standard-classes: clone regexp objects at runtime, on demand, to have the right parentage (164697, r=rogerl, sr=shaver).

This commit is contained in:
brendan%mozilla.org 2002-08-27 23:28:59 +00:00
Родитель 711c602803
Коммит 8d9d0a0155
5 изменённых файлов: 140 добавлений и 4 удалений

Просмотреть файл

@ -1233,6 +1233,18 @@ restart:
GC_MARK(cx, fp->scopeChain, "scope chain", NULL);
if (fp->sharpArray)
GC_MARK(cx, fp->sharpArray, "sharp array", NULL);
if (fp->objAtomMap) {
JSAtom **vector, *atom;
nslots = fp->objAtomMap->length;
vector = fp->objAtomMap->vector;
for (i = 0; i < nslots; i++) {
atom = vector[i];
if (atom)
GC_MARK_ATOM(cx, atom, NULL);
}
}
} while ((fp = fp->down) != NULL);
}

Просмотреть файл

@ -737,6 +737,7 @@ have_fun:
frame.sharpDepth = 0;
frame.sharpArray = NULL;
frame.dormantNext = NULL;
frame.objAtomMap = NULL;
/* Compute the 'this' parameter and store it in frame as frame.thisp. */
ok = ComputeThis(cx, thisp, &frame);
@ -991,6 +992,7 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
frame.sharpDepth = 0;
frame.flags = special;
frame.dormantNext = NULL;
frame.objAtomMap = NULL;
/*
* Here we wrap the call to js_Interpret with code to (conditionally)
@ -2918,12 +2920,113 @@ js_Interpret(JSContext *cx, jsval *result)
break;
case JSOP_OBJECT:
atom = GET_ATOM(cx, script, pc);
{
jsatomid atomIndex;
JSAtomMap *atomMap;
/*
* Get a suitable object from an atom mapped by the bytecode at pc.
*
* 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.
*
* 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.
*
* 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.
*/
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);
/* Compute the current global object in obj2. */
obj2 = fp->scopeChain;
while ((parent = OBJ_GET_PARENT(cx, obj2)) != NULL)
obj2 = parent;
/*
* If obj's parent is not obj2, we must clone obj so that it
* has the right parent, and therefore, the right prototype.
*
* Yes, this means we assume that the correct RegExp.prototype
* to which regexp instances (including literals) delegate can
* be distinguished solely by the instance's parent, which was
* set to the parent of the RegExp constructor function object
* when the instance was created. In other words,
*
* (/x/.__parent__ == RegExp.__parent__) implies
* (/x/.__proto__ == RegExp.prototype)
*
* (unless you assign a different object to RegExp.prototype
* at runtime, in which case, ECMA doesn't specify operation,
* 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.
*/
if (OBJ_GET_PARENT(cx, obj) != obj2) {
obj = js_CloneRegExpObject(cx, obj, obj2);
if (!obj) {
ok = JS_FALSE;
goto out;
}
atom = js_AtomizeObject(cx, obj, 0);
if (!atom) {
ok = JS_FALSE;
goto out;
}
}
/*
* 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;
}
atomMap->vector[atomIndex] = atom;
}
rval = ATOM_KEY(atom);
JS_ASSERT(JSVAL_IS_OBJECT(rval));
PUSH_OPND(rval);
obj = NULL;
break;
}
case JSOP_ZERO:
PUSH_OPND(JSVAL_ZERO);

Просмотреть файл

@ -67,6 +67,8 @@ struct JSStackFrame {
JSObject *sharpArray; /* scope for #n= initializer vars */
uint32 flags; /* frame flags -- see below */
JSStackFrame *dormantNext; /* next dormant frame chain */
JSAtomMap *objAtomMap; /* object atom map, non-null only if we
hit a regexp object literal */
};
typedef struct JSInlineFrame {

Просмотреть файл

@ -2976,4 +2976,23 @@ js_NewRegExpObject(JSContext *cx, JSTokenStream *ts,
return obj;
}
JSObject *
js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *parent)
{
JSObject *clone;
JSRegExp *re;
JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_RegExpClass);
clone = js_NewObject(cx, &js_RegExpClass, NULL, parent);
if (!clone)
return NULL;
re = JS_GetPrivate(cx, obj);
if (!JS_SetPrivate(cx, clone, re)) {
cx->newborn[GCX_OBJECT] = NULL; \
return NULL;
}
HOLD_REGEXP(cx, re);
return clone;
}
#endif /* JS_HAS_REGEXPS */

Просмотреть файл

@ -130,7 +130,7 @@ extern JSObject *
js_NewRegExpObject(JSContext *cx, JSTokenStream *ts,
jschar *chars, size_t length, uintN flags);
extern JSBool
js_XDRRegExp(JSXDRState *xdr, JSObject **objp);
extern JSObject *
js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *parent);
#endif /* jsregexp_h___ */