Bug 903193 - Part 6: Add ParallelExecution path to ArraySetLength. (r=bhackett)

This commit is contained in:
Shu-yu Guo 2013-10-08 15:14:04 -07:00
parent c3d9c59198
commit b0abb42ae8
10 changed files with 172 additions and 63 deletions

View File

@ -399,7 +399,7 @@ array_length_setter(JSContext *cx, HandleObject obj, HandleId id, bool strict, M
Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
MOZ_ASSERT(arr->lengthIsWritable(),
"setter shouldn't be called if property is non-writable");
return ArraySetLength(cx, arr, id, JSPROP_PERMANENT, vp, strict);
return ArraySetLength<SequentialExecution>(cx, arr, id, JSPROP_PERMANENT, vp, strict);
}
struct ReverseIndexComparator
@ -411,15 +411,30 @@ struct ReverseIndexComparator
}
};
template <ExecutionMode mode>
bool
js::CanonicalizeArrayLengthValue(JSContext *cx, HandleValue v, uint32_t *newLen)
js::CanonicalizeArrayLengthValue(typename ExecutionModeTraits<mode>::ContextType cx,
HandleValue v, uint32_t *newLen)
{
if (!ToUint32(cx, v, newLen))
return false;
double d;
if (!ToNumber(cx, v, &d))
return false;
if (mode == ParallelExecution) {
if (v.isObject())
return false;
if (!NonObjectToUint32(cx, v, newLen))
return false;
if (!NonObjectToNumber(cx, v, &d))
return false;
} else {
if (!ToUint32(cx->asJSContext(), v, newLen))
return false;
if (!ToNumber(cx->asJSContext(), v, &d))
return false;
}
if (d == *newLen)
return true;
@ -429,18 +444,28 @@ js::CanonicalizeArrayLengthValue(JSContext *cx, HandleValue v, uint32_t *newLen)
return false;
}
template bool
js::CanonicalizeArrayLengthValue<SequentialExecution>(JSContext *cx,
HandleValue v, uint32_t *newLen);
template bool
js::CanonicalizeArrayLengthValue<ParallelExecution>(ForkJoinSlice *slice,
HandleValue v, uint32_t *newLen);
/* ES6 20130308 draft 8.4.2.4 ArraySetLength */
template <ExecutionMode mode>
bool
js::ArraySetLength(JSContext *cx, Handle<ArrayObject*> arr, HandleId id, unsigned attrs,
HandleValue value, bool setterIsStrict)
js::ArraySetLength(typename ExecutionModeTraits<mode>::ContextType cxArg,
Handle<ArrayObject*> arr, HandleId id,
unsigned attrs, HandleValue value, bool setterIsStrict)
{
MOZ_ASSERT(id == NameToId(cx->names().length));
MOZ_ASSERT(cxArg->isThreadLocal(arr));
MOZ_ASSERT(id == NameToId(cxArg->names().length));
/* Steps 1-2 are irrelevant in our implementation. */
/* Steps 3-5. */
uint32_t newLen;
if (!CanonicalizeArrayLengthValue(cx, value, &newLen))
if (!CanonicalizeArrayLengthValue<mode>(cxArg, value, &newLen))
return false;
// Abort if we're being asked to change enumerability or configurability.
@ -455,14 +480,18 @@ js::ArraySetLength(JSContext *cx, Handle<ArrayObject*> arr, HandleId id, unsigne
if (!(attrs & JSPROP_PERMANENT) || (attrs & JSPROP_ENUMERATE)) {
if (!setterIsStrict)
return true;
return Throw(cx, id, JSMSG_CANT_REDEFINE_PROP);
// Bail for strict mode in parallel execution, as we need to go back
// to sequential mode to throw the error.
if (mode == ParallelExecution)
return false;
return Throw(cxArg->asJSContext(), id, JSMSG_CANT_REDEFINE_PROP);
}
/* Steps 6-7. */
bool lengthIsWritable = arr->lengthIsWritable();
#ifdef DEBUG
{
RootedShape lengthShape(cx, arr->nativeLookup(cx, id));
RootedShape lengthShape(cxArg, arr->nativeLookupPure(id));
MOZ_ASSERT(lengthShape);
MOZ_ASSERT(lengthShape->writable() == lengthIsWritable);
}
@ -475,16 +504,17 @@ js::ArraySetLength(JSContext *cx, Handle<ArrayObject*> arr, HandleId id, unsigne
if (newLen == oldLen)
return true;
if (!cx->isJSContext())
if (!cxArg->isJSContext())
return false;
if (setterIsStrict) {
return JS_ReportErrorFlagsAndNumber(cx->asJSContext(),
return JS_ReportErrorFlagsAndNumber(cxArg->asJSContext(),
JSREPORT_ERROR, js_GetErrorMessage, nullptr,
JSMSG_CANT_REDEFINE_ARRAY_LENGTH);
}
return JSObject::reportReadOnly(cx->asJSContext(), id, JSREPORT_STRICT | JSREPORT_WARNING);
return JSObject::reportReadOnly(cxArg->asJSContext(), id,
JSREPORT_STRICT | JSREPORT_WARNING);
}
/* Step 8. */
@ -512,7 +542,7 @@ js::ArraySetLength(JSContext *cx, Handle<ArrayObject*> arr, HandleId id, unsigne
if (oldInitializedLength > newLen)
arr->setDenseInitializedLength(newLen);
if (oldCapacity > newLen)
arr->shrinkElements(cx, newLen);
arr->shrinkElements(cxArg, newLen);
// We've done the work of deleting any dense elements needing
// deletion, and there are no sparse elements. Thus we can skip
@ -520,6 +550,13 @@ js::ArraySetLength(JSContext *cx, Handle<ArrayObject*> arr, HandleId id, unsigne
break;
}
// Bail from parallel execution if need to perform step 15, which is
// unsafe and isn't a common case.
if (mode == ParallelExecution)
return false;
JSContext *cx = cxArg->asJSContext();
// Step 15.
//
// Attempt to delete all elements above the new length, from greatest
@ -628,17 +665,29 @@ js::ArraySetLength(JSContext *cx, Handle<ArrayObject*> arr, HandleId id, unsigne
// API call on the floor here. Given that getter/setter will go away in
// the long run, with accessors replacing them both internally and at the
// API level, just run with this.
RootedShape lengthShape(cx, arr->nativeLookup(cx, id));
if (!JSObject::changeProperty(cx, arr, lengthShape, attrs,
JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_SHARED,
array_length_getter, array_length_setter))
RootedShape lengthShape(cxArg, mode == ParallelExecution
? arr->nativeLookupPure(id)
: arr->nativeLookup(cxArg->asJSContext(), id));
if (!JSObject::changeProperty<mode>(cxArg, arr, lengthShape, attrs,
JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_SHARED,
array_length_getter, array_length_setter))
{
return false;
}
RootedValue v(cx, NumberValue(newLen));
AddTypePropertyId(cx, arr, id, v);
ArrayObject::setLength(cx, arr, newLen);
RootedValue v(cxArg, NumberValue(newLen));
if (mode == ParallelExecution) {
// Adding the property type or overflowing int32 requires changing TI
// state.
if (!HasTypePropertyId(arr, id, v) || newLen > INT32_MAX)
return false;
arr->setLengthInt32(newLen);
} else {
JSContext *cx = cxArg->asJSContext();
AddTypePropertyId(cx, arr, id, v);
ArrayObject::setLength(cx, arr, newLen);
}
// All operations past here until the |!succeeded| code must be infallible,
// so that all element fields remain properly synchronized.
@ -659,12 +708,15 @@ js::ArraySetLength(JSContext *cx, Handle<ArrayObject*> arr, HandleId id, unsigne
// the already-required range check for |index < capacity| by making
// capacity of arrays with non-writable length never exceed the length.
if (arr->getDenseCapacity() > newLen) {
arr->shrinkElements(cx, newLen);
arr->shrinkElements(cxArg, newLen);
arr->getElementsHeader()->capacity = newLen;
}
}
if (setterIsStrict && !succeeded) {
// We can't have arrived here under ParallelExecution, as we have
// returned from the function before step 15 above.
JSContext *cx = cxArg->asJSContext();
RootedId elementId(cx);
if (!IndexToId(cx, newLen - 1, &elementId))
return false;
@ -674,6 +726,15 @@ js::ArraySetLength(JSContext *cx, Handle<ArrayObject*> arr, HandleId id, unsigne
return true;
}
template bool
js::ArraySetLength<SequentialExecution>(JSContext *cx, Handle<ArrayObject*> arr,
HandleId id, unsigned attrs, HandleValue value,
bool setterIsStrict);
template bool
js::ArraySetLength<ParallelExecution>(ForkJoinSlice *slice, Handle<ArrayObject*> arr,
HandleId id, unsigned attrs, HandleValue value,
bool setterIsStrict);
bool
js::WouldDefinePastNonwritableLength(ThreadSafeContext *cx,
HandleObject obj, uint32_t index, bool strict,

View File

@ -83,9 +83,13 @@ WouldDefinePastNonwritableLength(ThreadSafeContext *cx,
/*
* Canonicalize |vp| to a uint32_t value potentially suitable for use as an
* array length.
*
* For parallel execution we can only canonicalize non-object values.
*/
template <ExecutionMode mode>
extern bool
CanonicalizeArrayLengthValue(JSContext *cx, HandleValue v, uint32_t *canonicalized);
CanonicalizeArrayLengthValue(typename ExecutionModeTraits<mode>::ContextType cx,
HandleValue v, uint32_t *canonicalized);
extern bool
GetLengthProperty(JSContext *cx, HandleObject obj, uint32_t *lengthp);

View File

@ -2517,6 +2517,15 @@ TypeObject::markPropertyConfigured(ExclusiveContext *cx, jsid id)
types->setConfiguredProperty(cx);
}
bool
TypeObject::isPropertyConfigured(jsid id)
{
TypeSet *types = maybeGetProperty(id);
if (types)
return types->configuredProperty();
return false;
}
void
TypeObject::markStateChange(ExclusiveContext *cxArg)
{

View File

@ -1064,6 +1064,7 @@ struct TypeObject : gc::BarrieredCell<TypeObject>
void clearAddendum(ExclusiveContext *cx);
void clearNewScriptAddendum(ExclusiveContext *cx);
void clearTypedObjectAddendum(ExclusiveContext *cx);
bool isPropertyConfigured(jsid id);
void print();

View File

@ -533,6 +533,12 @@ MarkTypePropertyConfigured(ExclusiveContext *cx, HandleObject obj, jsid id)
}
}
inline bool
IsTypePropertyIdMarkedConfigured(JSObject *obj, jsid id)
{
return obj->type()->isPropertyConfigured(id);
}
/* Mark a state change on a particular object. */
inline void
MarkObjectStateChange(ExclusiveContext *cx, JSObject *obj)

View File

@ -884,7 +884,7 @@ DefinePropertyOnArray(JSContext *cx, Handle<ArrayObject*> arr, HandleId id, cons
RootedValue v(cx);
if (desc.hasValue()) {
uint32_t newLen;
if (!CanonicalizeArrayLengthValue(cx, desc.value(), &newLen))
if (!CanonicalizeArrayLengthValue<SequentialExecution>(cx, desc.value(), &newLen))
return false;
v.setNumber(newLen);
} else {
@ -908,7 +908,7 @@ DefinePropertyOnArray(JSContext *cx, Handle<ArrayObject*> arr, HandleId id, cons
attrs = attrs | JSPROP_READONLY;
}
return ArraySetLength(cx, arr, id, attrs, v, throwError);
return ArraySetLength<SequentialExecution>(cx, arr, id, attrs, v, throwError);
}
/* Step 3. */
@ -3532,12 +3532,10 @@ DefinePropertyOrElement(typename ExecutionModeTraits<mode>::ExclusiveContextType
if (obj->is<ArrayObject>()) {
Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
if (id == NameToId(cx->names().length)) {
if (mode == ParallelExecution)
if (mode == SequentialExecution && !cx->shouldBeJSContext())
return false;
if (!cx->shouldBeJSContext())
return false;
return ArraySetLength(cx->asJSContext(), arr, id, attrs, value, setterIsStrict);
return ArraySetLength<mode>(ExecutionModeTraits<mode>::toContextType(cx), arr, id,
attrs, value, setterIsStrict);
}
uint32_t index;
@ -3634,14 +3632,14 @@ js::DefineNativeProperty(ExclusiveContext *cx, HandleObject obj, HandleId id, Ha
shape = obj->nativeLookup(cx, id);
}
if (shape->isAccessorDescriptor()) {
shape = JSObject::changeProperty(cx, obj, shape, attrs,
JSPROP_GETTER | JSPROP_SETTER,
(attrs & JSPROP_GETTER)
? getter
: shape->getter(),
(attrs & JSPROP_SETTER)
? setter
: shape->setter());
shape = JSObject::changeProperty<SequentialExecution>(cx, obj, shape, attrs,
JSPROP_GETTER | JSPROP_SETTER,
(attrs & JSPROP_GETTER)
? getter
: shape->getter(),
(attrs & JSPROP_SETTER)
? setter
: shape->setter());
if (!shape)
return false;
} else {
@ -4813,12 +4811,8 @@ baseops::SetPropertyHelper(typename ExecutionModeTraits<mode>::ContextType cxArg
}
if (obj->is<ArrayObject>() && id == NameToId(cxArg->names().length)) {
if (mode == ParallelExecution)
return false;
JSContext *cx = cxArg->asJSContext();
Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
return ArraySetLength(cx, arr, id, attrs, vp, strict);
Rooted<ArrayObject*> arr(cxArg, &obj->as<ArrayObject>());
return ArraySetLength<mode>(cxArg, arr, id, attrs, vp, strict);
}
if (!shape) {

View File

@ -899,9 +899,11 @@ class JSObject : public js::ObjectImpl
unsigned flags, int shortid);
/* Change the given property into a sibling with the same id in this scope. */
static js::Shape *changeProperty(js::ExclusiveContext *cx, js::HandleObject obj,
js::HandleShape shape, unsigned attrs, unsigned mask,
JSPropertyOp getter, JSStrictPropertyOp setter);
template <js::ExecutionMode mode>
static js::Shape *
changeProperty(typename js::ExecutionModeTraits<mode>::ExclusiveContextType cx,
js::HandleObject obj, js::HandleShape shape, unsigned attrs, unsigned mask,
JSPropertyOp getter, JSStrictPropertyOp setter);
static inline bool changePropertyAttributes(JSContext *cx, js::HandleObject obj,
js::HandleShape shape, unsigned attrs);

View File

@ -34,7 +34,8 @@ JSObject::setGenericAttributes(JSContext *cx, js::HandleObject obj,
JSObject::changePropertyAttributes(JSContext *cx, js::HandleObject obj,
js::HandleShape shape, unsigned attrs)
{
return !!changeProperty(cx, obj, shape, attrs, 0, shape->getter(), shape->setter());
return !!changeProperty<js::SequentialExecution>(cx, obj, shape, attrs, 0,
shape->getter(), shape->setter());
}
/* static */ inline bool

View File

@ -676,9 +676,11 @@ class ArrayBufferObject;
* |setterIsStrict| indicates whether invalid changes will cause a TypeError
* to be thrown.
*/
template <ExecutionMode mode>
extern bool
ArraySetLength(JSContext *cx, Handle<ArrayObject*> obj, HandleId id, unsigned attrs,
HandleValue value, bool setterIsStrict);
ArraySetLength(typename ExecutionModeTraits<mode>::ContextType cx,
Handle<ArrayObject*> obj, HandleId id,
unsigned attrs, HandleValue value, bool setterIsStrict);
/*
* Elements header used for all native objects. The elements component of such
@ -768,9 +770,11 @@ class ObjectElements
friend class ArrayBufferObject;
friend class Nursery;
template <ExecutionMode mode>
friend bool
ArraySetLength(JSContext *cx, Handle<ArrayObject*> obj, HandleId id, unsigned attrs,
HandleValue value, bool setterIsStrict);
ArraySetLength(typename ExecutionModeTraits<mode>::ContextType cx,
Handle<ArrayObject*> obj, HandleId id,
unsigned attrs, HandleValue value, bool setterIsStrict);
/* See Flags enum above. */
uint32_t flags;

View File

@ -904,11 +904,14 @@ JSObject::putProperty<ParallelExecution>(ForkJoinSlice *cx,
uint32_t slot, unsigned attrs,
unsigned flags, int shortid);
template <ExecutionMode mode>
/* static */ Shape *
JSObject::changeProperty(ExclusiveContext *cx, HandleObject obj, HandleShape shape, unsigned attrs,
JSObject::changeProperty(typename ExecutionModeTraits<mode>::ExclusiveContextType cx,
HandleObject obj, HandleShape shape, unsigned attrs,
unsigned mask, PropertyOp getter, StrictPropertyOp setter)
{
JS_ASSERT(obj->nativeContains(cx, shape));
JS_ASSERT(cx->isThreadLocal(obj));
JS_ASSERT(obj->nativeContainsPure(shape));
attrs |= shape->attrs & mask;
@ -916,9 +919,22 @@ JSObject::changeProperty(ExclusiveContext *cx, HandleObject obj, HandleShape sha
JS_ASSERT(!((attrs ^ shape->attrs) & JSPROP_SHARED) ||
!(attrs & JSPROP_SHARED));
types::MarkTypePropertyConfigured(cx, obj, shape->propid());
if (attrs & (JSPROP_GETTER | JSPROP_SETTER))
types::AddTypePropertyId(cx, obj, shape->propid(), types::Type::UnknownType());
if (mode == ParallelExecution) {
if (!types::IsTypePropertyIdMarkedConfigured(obj, shape->propid()))
return nullptr;
} else {
types::MarkTypePropertyConfigured(cx->asExclusiveContext(), obj, shape->propid());
}
if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
if (mode == ParallelExecution) {
if (!types::HasTypePropertyId(obj, shape->propid(), types::Type::UnknownType()))
return nullptr;
} else {
types::AddTypePropertyId(cx->asExclusiveContext(), obj, shape->propid(),
types::Type::UnknownType());
}
}
if (getter == JS_PropertyStub)
getter = nullptr;
@ -938,14 +954,25 @@ JSObject::changeProperty(ExclusiveContext *cx, HandleObject obj, HandleShape sha
* putProperty won't re-allocate it.
*/
RootedId propid(cx, shape->propid());
Shape *newShape = putProperty<SequentialExecution>(cx, obj, propid, getter, setter,
shape->maybeSlot(), attrs, shape->flags,
shape->maybeShortid());
Shape *newShape = putProperty<mode>(cx, obj, propid, getter, setter,
shape->maybeSlot(), attrs, shape->flags,
shape->maybeShortid());
obj->checkShapeConsistency();
return newShape;
}
template /* static */ Shape *
JSObject::changeProperty<SequentialExecution>(ExclusiveContext *cx,
HandleObject obj, HandleShape shape,
unsigned attrs, unsigned mask,
PropertyOp getter, StrictPropertyOp setter);
template /* static */ Shape *
JSObject::changeProperty<ParallelExecution>(ForkJoinSlice *slice,
HandleObject obj, HandleShape shape,
unsigned attrs, unsigned mask,
PropertyOp getter, StrictPropertyOp setter);
bool
JSObject::removeProperty(ExclusiveContext *cx, jsid id_)
{