Bug 934450 - Allow objects to have copy on write elements, r=billm,jandem.

This commit is contained in:
Brian Hackett
2014-08-19 22:25:37 -08:00
parent 4a1eb84a68
commit f7d8523463
40 changed files with 844 additions and 88 deletions

View File

@@ -1365,8 +1365,11 @@ JSObject::sealOrFreeze(JSContext *cx, HandleObject obj, ImmutabilityType it)
// arrays with non-writable length. We don't need to do anything special
// for that, because capacity was zeroed out by preventExtensions. (See
// the assertion before the if-else above.)
if (it == FREEZE && obj->is<ArrayObject>())
if (it == FREEZE && obj->is<ArrayObject>()) {
if (!obj->maybeCopyElementsForWrite(cx))
return false;
obj->getElementsHeader()->setNonwritableArrayLength();
}
return true;
}
@@ -2315,15 +2318,48 @@ js::XDRObjectLiteral(XDRState<XDR_DECODE> *xdr, MutableHandleObject obj);
JSObject *
js::CloneObjectLiteral(JSContext *cx, HandleObject parent, HandleObject srcObj)
{
Rooted<TypeObject*> typeObj(cx);
typeObj = cx->getNewType(&JSObject::class_, TaggedProto(cx->global()->getOrCreateObjectPrototype(cx)));
if (srcObj->getClass() == &JSObject::class_) {
AllocKind kind = GetBackgroundAllocKind(GuessObjectGCKind(srcObj->numFixedSlots()));
JS_ASSERT_IF(srcObj->isTenured(), kind == srcObj->tenuredGetAllocKind());
JS_ASSERT(srcObj->getClass() == &JSObject::class_);
AllocKind kind = GetBackgroundAllocKind(GuessObjectGCKind(srcObj->numFixedSlots()));
JS_ASSERT_IF(srcObj->isTenured(), kind == srcObj->tenuredGetAllocKind());
JSObject *proto = cx->global()->getOrCreateObjectPrototype(cx);
if (!proto)
return nullptr;
Rooted<TypeObject*> typeObj(cx, cx->getNewType(&JSObject::class_, TaggedProto(proto)));
if (!typeObj)
return nullptr;
RootedShape shape(cx, srcObj->lastProperty());
return NewReshapedObject(cx, typeObj, parent, kind, shape);
RootedShape shape(cx, srcObj->lastProperty());
return NewReshapedObject(cx, typeObj, parent, kind, shape);
}
JS_ASSERT(srcObj->is<ArrayObject>());
JS_ASSERT(srcObj->denseElementsAreCopyOnWrite());
JS_ASSERT(srcObj->getElementsHeader()->ownerObject() == srcObj);
size_t length = srcObj->as<ArrayObject>().length();
RootedObject res(cx, NewDenseAllocatedArray(cx, length, nullptr, MaybeSingletonObject));
if (!res)
return nullptr;
RootedId id(cx);
RootedValue value(cx);
for (size_t i = 0; i < length; i++) {
// The only markable values in copy on write arrays are atoms, which
// can be freely copied between compartments.
value = srcObj->getDenseElement(i);
JS_ASSERT_IF(value.isMarkable(),
cx->runtime()->isAtomsZone(value.toGCThing()->tenuredZone()));
id = INT_TO_JSID(i);
if (!JSObject::defineGeneric(cx, res, id, value, nullptr, nullptr, JSPROP_ENUMERATE))
return nullptr;
}
if (!ObjectElements::MakeElementsCopyOnWrite(cx, res))
return nullptr;
return res;
}
struct JSObject::TradeGutsReserved {
@@ -3063,6 +3099,9 @@ JSObject::shrinkSlots(ThreadSafeContext *cx, HandleObject obj, uint32_t oldCount
/* static */ bool
JSObject::sparsifyDenseElement(ExclusiveContext *cx, HandleObject obj, uint32_t index)
{
if (!obj->maybeCopyElementsForWrite(cx))
return false;
RootedValue value(cx, obj->getDenseElement(index));
JS_ASSERT(!value.isMagic(JS_ELEMENTS_HOLE));
@@ -3083,6 +3122,9 @@ JSObject::sparsifyDenseElement(ExclusiveContext *cx, HandleObject obj, uint32_t
/* static */ bool
JSObject::sparsifyDenseElements(js::ExclusiveContext *cx, HandleObject obj)
{
if (!obj->maybeCopyElementsForWrite(cx))
return false;
uint32_t initialized = obj->getDenseInitializedLength();
/* Create new properties with the value of non-hole dense elements. */
@@ -3201,6 +3243,9 @@ JSObject::maybeDensifySparseElements(js::ExclusiveContext *cx, HandleObject obj)
* properties into dense elements.
*/
if (!obj->maybeCopyElementsForWrite(cx))
return ED_FAILED;
if (newInitializedLength > obj->getDenseCapacity()) {
if (!obj->growElements(cx, newInitializedLength))
return ED_FAILED;
@@ -3382,6 +3427,8 @@ JSObject::growElements(ThreadSafeContext *cx, uint32_t reqCapacity)
{
JS_ASSERT(nonProxyIsExtensible());
JS_ASSERT(canHaveNonEmptyElements());
if (denseElementsAreCopyOnWrite())
MOZ_CRASH();
uint32_t oldCapacity = getDenseCapacity();
JS_ASSERT(oldCapacity < reqCapacity);
@@ -3444,6 +3491,8 @@ JSObject::shrinkElements(ThreadSafeContext *cx, uint32_t reqCapacity)
{
JS_ASSERT(cx->isThreadLocal(this));
JS_ASSERT(canHaveNonEmptyElements());
if (denseElementsAreCopyOnWrite())
MOZ_CRASH();
if (!hasDynamicElements())
return;
@@ -3471,6 +3520,38 @@ JSObject::shrinkElements(ThreadSafeContext *cx, uint32_t reqCapacity)
elements = newheader->elements();
}
/* static */ bool
JSObject::CopyElementsForWrite(ThreadSafeContext *cx, JSObject *obj)
{
JS_ASSERT(obj->denseElementsAreCopyOnWrite());
// The original owner of a COW elements array should never be modified.
JS_ASSERT(obj->getElementsHeader()->ownerObject() != obj);
uint32_t initlen = obj->getDenseInitializedLength();
uint32_t allocated = initlen + ObjectElements::VALUES_PER_HEADER;
uint32_t newAllocated = goodAllocated(allocated);
uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER;
if (newCapacity >= NELEMENTS_LIMIT)
return false;
ObjectElements *newheader = AllocateElements(cx, obj, newAllocated);
if (!newheader)
return false;
js_memcpy(newheader, obj->getElementsHeader(),
(ObjectElements::VALUES_PER_HEADER + initlen) * sizeof(Value));
newheader->capacity = newCapacity;
newheader->clearCopyOnWrite();
obj->elements = newheader->elements();
Debug_SetSlotRangeToCrashOnTouch(obj->elements + initlen, newCapacity - initlen);
return true;
}
bool
js::SetClassAndProto(JSContext *cx, HandleObject obj,
const Class *clasp, Handle<js::TaggedProto> proto,
@@ -3967,6 +4048,9 @@ CallAddPropertyHookDense(typename ExecutionModeTraits<mode>::ExclusiveContextTyp
if (!cx->shouldBeJSContext())
return false;
if (!obj->maybeCopyElementsForWrite(cx))
return false;
/* Make a local copy of value so addProperty can mutate its inout parameter. */
RootedValue value(cx, nominal);
@@ -4104,6 +4188,9 @@ DefinePropertyOrElement(typename ExecutionModeTraits<mode>::ExclusiveContextType
if (mode == ParallelExecution)
return false;
if (!obj->maybeCopyElementsForWrite(cx))
return false;
ExclusiveContext *ncx = cx->asExclusiveContext();
uint32_t index = JSID_TO_INT(id);
JSObject::removeDenseElementForSparseIndex(ncx, obj, index);
@@ -5384,6 +5471,9 @@ baseops::SetPropertyHelper(typename ExecutionModeTraits<mode>::ContextType cxArg
return true;
}
if (!obj->maybeCopyElementsForWrite(cxArg))
return false;
if (mode == ParallelExecution)
return obj->setDenseElementIfHasType(index, vp);
@@ -5540,6 +5630,9 @@ baseops::DeleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succe
if (!succeeded)
return true;
if (!obj->maybeCopyElementsForWrite(cx))
return false;
obj->setDenseElementHole(cx, JSID_TO_INT(id));
return js_SuppressDeletedProperty(cx, obj, id);
}
@@ -6253,7 +6346,8 @@ JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::Objects
if (hasDynamicElements()) {
js::ObjectElements *elements = getElementsHeader();
sizes->mallocHeapElementsNonAsmJS += mallocSizeOf(elements);
if (!elements->isCopyOnWrite() || elements->ownerObject() == this)
sizes->mallocHeapElementsNonAsmJS += mallocSizeOf(elements);
}
// Other things may be measured in the future if DMD indicates it is worthwhile.