Bug 561954 - Avoid costly deep bail by aborting earlier on bad global prop access (r=jorendorff)

This commit is contained in:
Luke Wagner 2010-11-18 10:49:45 -08:00
parent 96d0c8e0fa
commit 319ccaa65d
6 changed files with 106 additions and 4 deletions

View File

@ -0,0 +1,19 @@
/* Touch/init early so global shape doesn't change in loop */
var SetOnIter = HOTLOOP - 1;
var x = 3;
var i = 0;
assertEq(true, true);
for (i = 0; i < SetOnIter + 10; ++i) {
x = 3;
setGlobalPropIf(i == SetOnIter, 'x', 'pretty');
assertEq(x == 'pretty', i == SetOnIter);
x = 3;
}
for (i = 0; i < SetOnIter + 10; ++i) {
x = 3;
defGlobalPropIf(i == SetOnIter, 'x', { value:'pretty' });
assertEq(x == 'pretty', i == SetOnIter);
x = 3;
}

View File

@ -435,6 +435,14 @@ IsFunctionObject(const js::Value &v, JSFunction **fun)
return b;
}
extern JS_ALWAYS_INLINE bool
SameTraceType(const Value &lhs, const Value &rhs)
{
return SameType(lhs, rhs) &&
(lhs.isPrimitive() ||
lhs.toObject().isFunction() == rhs.toObject().isFunction());
}
/*
* Macro to access the private slot of the function object after the slot is
* initialized.

View File

@ -4458,8 +4458,10 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &valu
}
/* Store value before calling addProperty, in case the latter GC's. */
if (obj->containsSlot(shape->slot))
if (obj->containsSlot(shape->slot)) {
AbortRecordingIfChangingGlobalSlotType(cx, obj, value, shape->slot);
obj->nativeSetSlot(shape->slot, value);
}
/* XXXbe called with lock held */
valueCopy = value;
@ -4922,6 +4924,7 @@ js_NativeSet(JSContext *cx, JSObject *obj, const Shape *shape, bool added, Value
slot = shape->slot;
if (slot != SHAPE_INVALID_SLOT) {
JS_ASSERT(obj->containsSlot(slot));
AbortRecordingIfChangingGlobalSlotType(cx, obj, *vp, slot);
/* If shape has a stub setter, keep obj locked and just store *vp. */
if (shape->hasDefaultSetter()) {

View File

@ -11514,7 +11514,7 @@ TraceRecorder::record_JSOP_DELNAME()
return ARECORD_STOP;
}
JSBool JS_FASTCALL
static JSBool JS_FASTCALL
DeleteIntKey(JSContext* cx, JSObject* obj, int32 i, JSBool strict)
{
LeaveTraceIfGlobalObject(cx, obj);
@ -11536,7 +11536,7 @@ DeleteIntKey(JSContext* cx, JSObject* obj, int32 i, JSBool strict)
JS_DEFINE_CALLINFO_4(extern, BOOL_FAIL, DeleteIntKey, CONTEXT, OBJECT, INT32, BOOL,
0, ACCSET_STORE_ANY)
JSBool JS_FASTCALL
static JSBool JS_FASTCALL
DeleteStrKey(JSContext* cx, JSObject* obj, JSString* str, JSBool strict)
{
LeaveTraceIfGlobalObject(cx, obj);

View File

@ -1839,4 +1839,18 @@ struct TraceVisStateObj {
#endif /* !JS_TRACER */
namespace js {
static JS_INLINE void
AbortRecordingIfChangingGlobalSlotType(JSContext *cx, JSObject *obj,
const Value &v, unsigned slot)
{
#ifdef JS_TRACER
if (TRACE_RECORDER(cx) && !obj->parent && !SameTraceType(v, obj->nativeGetSlot(slot)))
AbortRecording(cx, "Global slot type changed outside tracer supervision");
#endif
}
} /* namespace js */
#endif /* jstracer_h___ */

View File

@ -4204,6 +4204,59 @@ Deserialize(JSContext *cx, uintN argc, jsval *vp)
return true;
}
JSBool
SetGlobalPropIf(JSContext *cx, uintN argc, jsval *vp)
{
if (argc != 3) {
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "setGlobalPropIf");
return false;
}
jsval *argv = JS_ARGV(cx, vp);
JSBool doSet;
if (!JS_ValueToBoolean(cx, argv[0], &doSet))
return false;
JS_SET_RVAL(cx, vp, JSVAL_VOID);
if (!doSet)
return true;
jsid id;
if (!JS_ValueToId(cx, argv[1], &id))
return false;
JSObject *global = JS_GetGlobalForScopeChain(cx);
return global && JS_SetPropertyById(cx, global, id, &argv[2]);
}
JSBool
DefGlobalPropIf(JSContext *cx, uintN argc, jsval *vp)
{
if (argc != 3) {
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "setGlobalPropIf");
return false;
}
jsval *argv = JS_ARGV(cx, vp);
JSBool doSet;
if (!JS_ValueToBoolean(cx, argv[0], &doSet))
return false;
JS_SET_RVAL(cx, vp, JSVAL_VOID);
if (!doSet)
return true;
jsid id;
if (!JS_ValueToId(cx, argv[1], &id))
return false;
JSObject *global = JS_GetGlobalForScopeChain(cx);
JSBool ignore;
return global && JS_DefineOwnProperty(cx, global, id, argv[2], &ignore);
}
/* We use a mix of JS_FS and JS_FN to test both kinds of natives. */
static JSFunctionSpec shell_functions[] = {
JS_FN("version", Version, 0,0),
@ -4301,6 +4354,8 @@ static JSFunctionSpec shell_functions[] = {
JS_FN("wrap", Wrap, 1,0),
JS_FN("serialize", Serialize, 1,0),
JS_FN("deserialize", Deserialize, 1,0),
JS_FN("setGlobalPropIf",SetGlobalPropIf,3,0),
JS_FN("defGlobalPropIf",DefGlobalPropIf,3,0),
JS_FS_END
};
@ -4429,7 +4484,10 @@ static const char *const shell_help_messages[] = {
"parent(obj) Returns the parent of obj.\n",
"wrap(obj) Wrap an object into a noop wrapper.\n",
"serialize(sd) Serialize sd using JS_WriteStructuredClone. Returns a TypedArray.\n",
"deserialize(a) Deserialize data generated by serialize.\n"
"deserialize(a) Deserialize data generated by serialize.\n",
"setGlobalPropIf(b,id,v) If b, get the global object o and perform o[id] = v.\n",
"defGlobalPropIf(b,id,dsc)If b, get the global object o and perform\n"
" Object.defineProperty(o, id, dsc).\n"
};
/* Help messages must match shell functions. */