"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "js/src/vm/UnboxedObject.cpp" between
UXP-2019.03.27.tar.gz and UXP-2019.06.08.tar.gz

About: Basilisk is a full-featured, optimized browser derived from the code behind the pre-Servo Firefox browser (29–56 era). UXP = Unified XUL Platform. Source code.

UnboxedObject.cpp  (UXP-2019.03.27):UnboxedObject.cpp  (UXP-2019.06.08)
skipping to change at line 1658 skipping to change at line 1658
&UnboxedArrayObjectClassOps, &UnboxedArrayObjectClassOps,
JS_NULL_CLASS_SPEC, JS_NULL_CLASS_SPEC,
&UnboxedArrayObjectClassExtension, &UnboxedArrayObjectClassExtension,
&UnboxedArrayObjectObjectOps &UnboxedArrayObjectObjectOps
}; };
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
// API // API
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
static bool
UnboxedTypeIncludes(JSValueType supertype, JSValueType subtype)
{
if (supertype == JSVAL_TYPE_DOUBLE && subtype == JSVAL_TYPE_INT32)
return true;
if (supertype == JSVAL_TYPE_OBJECT && subtype == JSVAL_TYPE_NULL)
return true;
return false;
}
static bool
CombineUnboxedTypes(const Value& value, JSValueType* existing)
{
JSValueType type = value.isDouble() ? JSVAL_TYPE_DOUBLE : value.extractNonDo
ubleType();
if (*existing == JSVAL_TYPE_MAGIC || *existing == type || UnboxedTypeInclude
s(type, *existing)) {
*existing = type;
return true;
}
if (UnboxedTypeIncludes(*existing, type))
return true;
return false;
}
// Return whether the property names and types in layout are a subset of the
// specified vector.
static bool
PropertiesAreSuperset(const UnboxedLayout::PropertyVector& properties, UnboxedLa
yout* layout)
{
for (size_t i = 0; i < layout->properties().length(); i++) {
const UnboxedLayout::Property& layoutProperty = layout->properties()[i];
bool found = false;
for (size_t j = 0; j < properties.length(); j++) {
if (layoutProperty.name == properties[j].name) {
found = (layoutProperty.type == properties[j].type);
break;
}
}
if (!found)
return false;
}
return true;
}
static bool
CombinePlainObjectProperties(PlainObject* obj, Shape* templateShape,
UnboxedLayout::PropertyVector& properties)
{
// All preliminary objects must have been created with enough space to
// fill in their unboxed data inline. This is ensured either by using
// the largest allocation kind (which limits the maximum size of an
// unboxed object), or by using an allocation kind that covers all
// properties in the template, as the space used by unboxed properties
// is less than or equal to that used by boxed properties.
MOZ_ASSERT(gc::GetGCKindSlots(obj->asTenured().getAllocKind()) >=
Min(NativeObject::MAX_FIXED_SLOTS, templateShape->slotSpan()));
if (obj->lastProperty() != templateShape || obj->hasDynamicElements()) {
// Only use an unboxed representation if all created objects match
// the template shape exactly.
return false;
}
for (size_t i = 0; i < templateShape->slotSpan(); i++) {
Value val = obj->getSlot(i);
JSValueType& existing = properties[i].type;
if (!CombineUnboxedTypes(val, &existing))
return false;
}
return true;
}
static bool
CombineArrayObjectElements(ExclusiveContext* cx, ArrayObject* obj, JSValueType*
elementType)
{
if (obj->inDictionaryMode() ||
obj->lastProperty()->propid() != AtomToId(cx->names().length) ||
!obj->lastProperty()->previous()->isEmptyShape())
{
// Only use an unboxed representation if the object has no properties.
return false;
}
for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) {
Value val = obj->getDenseElement(i);
// For now, unboxed arrays cannot have holes.
if (val.isMagic(JS_ELEMENTS_HOLE))
return false;
if (!CombineUnboxedTypes(val, elementType))
return false;
}
return true;
}
static size_t
ComputePlainObjectLayout(ExclusiveContext* cx, Shape* templateShape,
UnboxedLayout::PropertyVector& properties)
{
// Fill in the names for all the object's properties.
for (Shape::Range<NoGC> r(templateShape); !r.empty(); r.popFront()) {
size_t slot = r.front().slot();
MOZ_ASSERT(!properties[slot].name);
properties[slot].name = JSID_TO_ATOM(r.front().propid())->asPropertyName
();
}
// Fill in all the unboxed object's property offsets.
uint32_t offset = 0;
// Search for an existing unboxed layout which is a subset of this one.
// If there are multiple such layouts, use the largest one. If we're able
// to find such a layout, use the same property offsets for the shared
// properties, which will allow us to generate better code if the objects
// have a subtype/supertype relation and are accessed at common sites.
UnboxedLayout* bestExisting = nullptr;
for (UnboxedLayout* existing : cx->compartment()->unboxedLayouts) {
if (PropertiesAreSuperset(properties, existing)) {
if (!bestExisting ||
existing->properties().length() > bestExisting->properties().len
gth())
{
bestExisting = existing;
}
}
}
if (bestExisting) {
for (size_t i = 0; i < bestExisting->properties().length(); i++) {
const UnboxedLayout::Property& existingProperty = bestExisting->prop
erties()[i];
for (size_t j = 0; j < templateShape->slotSpan(); j++) {
if (existingProperty.name == properties[j].name) {
MOZ_ASSERT(existingProperty.type == properties[j].type);
properties[j].offset = existingProperty.offset;
}
}
}
offset = bestExisting->size();
}
// Order remaining properties from the largest down for the best space
// utilization.
static const size_t typeSizes[] = { 8, 4, 1 };
for (size_t i = 0; i < ArrayLength(typeSizes); i++) {
size_t size = typeSizes[i];
for (size_t j = 0; j < templateShape->slotSpan(); j++) {
if (properties[j].offset != UINT32_MAX)
continue;
JSValueType type = properties[j].type;
if (UnboxedTypeSize(type) == size) {
offset = JS_ROUNDUP(offset, size);
properties[j].offset = offset;
offset += size;
}
}
}
// The final offset is the amount of data needed by the object.
return offset;
}
static bool
SetLayoutTraceList(ExclusiveContext* cx, UnboxedLayout* layout)
{
// Figure out the offsets of any objects or string properties.
Vector<int32_t, 8, SystemAllocPolicy> objectOffsets, stringOffsets;
for (size_t i = 0; i < layout->properties().length(); i++) {
const UnboxedLayout::Property& property = layout->properties()[i];
MOZ_ASSERT(property.offset != UINT32_MAX);
if (property.type == JSVAL_TYPE_OBJECT) {
if (!objectOffsets.append(property.offset))
return false;
} else if (property.type == JSVAL_TYPE_STRING) {
if (!stringOffsets.append(property.offset))
return false;
}
}
// Construct the layout's trace list.
if (!objectOffsets.empty() || !stringOffsets.empty()) {
Vector<int32_t, 8, SystemAllocPolicy> entries;
if (!entries.appendAll(stringOffsets) ||
!entries.append(-1) ||
!entries.appendAll(objectOffsets) ||
!entries.append(-1) ||
!entries.append(-1))
{
return false;
}
int32_t* traceList = cx->zone()->pod_malloc<int32_t>(entries.length());
if (!traceList)
return false;
PodCopy(traceList, entries.begin(), entries.length());
layout->setTraceList(traceList);
}
return true;
}
static inline Value static inline Value
NextValue(Handle<GCVector<Value>> values, size_t* valueCursor) NextValue(Handle<GCVector<Value>> values, size_t* valueCursor)
{ {
return values[(*valueCursor)++]; return values[(*valueCursor)++];
} }
static bool
GetValuesFromPreliminaryArrayObject(ArrayObject* obj, MutableHandle<GCVector<Val
ue>> values)
{
if (!values.append(Int32Value(obj->length())))
return false;
if (!values.append(Int32Value(obj->getDenseInitializedLength())))
return false;
for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) {
if (!values.append(obj->getDenseElement(i)))
return false;
}
return true;
}
void void
UnboxedArrayObject::fillAfterConvert(ExclusiveContext* cx, UnboxedArrayObject::fillAfterConvert(ExclusiveContext* cx,
Handle<GCVector<Value>> values, size_t* val ueCursor) Handle<GCVector<Value>> values, size_t* val ueCursor)
{ {
MOZ_ASSERT(CapacityArray[1] == 0); MOZ_ASSERT(CapacityArray[1] == 0);
setCapacityIndex(1); setCapacityIndex(1);
setInitializedLengthNoBarrier(0); setInitializedLengthNoBarrier(0);
setInlineElements(); setInlineElements();
setLength(cx, NextValue(values, valueCursor).toInt32()); setLength(cx, NextValue(values, valueCursor).toInt32());
skipping to change at line 1904 skipping to change at line 1689
AutoEnterOOMUnsafeRegion oomUnsafe; AutoEnterOOMUnsafeRegion oomUnsafe;
if (!growElements(cx, initlen)) if (!growElements(cx, initlen))
oomUnsafe.crash("UnboxedArrayObject::fillAfterConvert"); oomUnsafe.crash("UnboxedArrayObject::fillAfterConvert");
setInitializedLength(initlen); setInitializedLength(initlen);
for (size_t i = 0; i < size_t(initlen); i++) for (size_t i = 0; i < size_t(initlen); i++)
JS_ALWAYS_TRUE(initElement(cx, i, NextValue(values, valueCursor))); JS_ALWAYS_TRUE(initElement(cx, i, NextValue(values, valueCursor)));
} }
static bool
GetValuesFromPreliminaryPlainObject(PlainObject* obj, MutableHandle<GCVector<Val
ue>> values)
{
for (size_t i = 0; i < obj->slotSpan(); i++) {
if (!values.append(obj->getSlot(i)))
return false;
}
return true;
}
void void
UnboxedPlainObject::fillAfterConvert(ExclusiveContext* cx, UnboxedPlainObject::fillAfterConvert(ExclusiveContext* cx,
Handle<GCVector<Value>> values, size_t* val ueCursor) Handle<GCVector<Value>> values, size_t* val ueCursor)
{ {
initExpando(); initExpando();
memset(data(), 0, layout().size()); memset(data(), 0, layout().size());
for (size_t i = 0; i < layout().properties().length(); i++) for (size_t i = 0; i < layout().properties().length(); i++)
JS_ALWAYS_TRUE(setValue(cx, layout().properties()[i], NextValue(values, valueCursor))); JS_ALWAYS_TRUE(setValue(cx, layout().properties()[i], NextValue(values, valueCursor)));
} }
bool
js::TryConvertToUnboxedLayout(ExclusiveContext* cx, AutoEnterAnalysis& enter, Sh
ape* templateShape,
ObjectGroup* group, PreliminaryObjectArray* object
s)
{
bool isArray = !templateShape;
// Unboxed arrays are nightly only for now. The getenv() call will be
// removed when they are on by default. See bug 1153266.
if (isArray) {
#ifdef NIGHTLY_BUILD
if (!getenv("JS_OPTION_USE_UNBOXED_ARRAYS")) {
if (!cx->options().unboxedArrays())
return true;
}
#else
return true;
#endif
} else {
if (jit::JitOptions.disableUnboxedObjects)
return true;
}
MOZ_ASSERT_IF(templateShape, !templateShape->getObjectFlags());
if (group->runtimeFromAnyThread()->isSelfHostingGlobal(cx->global()))
return true;
if (!isArray && templateShape->slotSpan() == 0)
return true;
UnboxedLayout::PropertyVector properties;
if (!isArray) {
if (!properties.appendN(UnboxedLayout::Property(), templateShape->slotSp
an()))
return false;
}
JSValueType elementType = JSVAL_TYPE_MAGIC;
size_t objectCount = 0;
for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) {
JSObject* obj = objects->get(i);
if (!obj)
continue;
if (obj->isSingleton() || obj->group() != group)
return true;
objectCount++;
if (isArray) {
if (!CombineArrayObjectElements(cx, &obj->as<ArrayObject>(), &elemen
tType))
return true;
} else {
if (!CombinePlainObjectProperties(&obj->as<PlainObject>(), templateS
hape, properties))
return true;
}
}
size_t layoutSize = 0;
if (isArray) {
// Don't use an unboxed representation if we couldn't determine an
// element type for the objects.
if (UnboxedTypeSize(elementType) == 0)
return true;
} else {
if (objectCount <= 1) {
// If only one of the objects has been created, it is more likely
// to have new properties added later. This heuristic is not used
// for array objects, where we might want an unboxed representation
// even if there is only one large array.
return true;
}
for (size_t i = 0; i < templateShape->slotSpan(); i++) {
// We can't use an unboxed representation if e.g. all the objects ha
ve
// a null value for one of the properties, as we can't decide what t
ype
// it is supposed to have.
if (UnboxedTypeSize(properties[i].type) == 0)
return true;
}
// Make sure that all properties on the template shape are property
// names, and not indexes.
for (Shape::Range<NoGC> r(templateShape); !r.empty(); r.popFront()) {
jsid id = r.front().propid();
uint32_t dummy;
if (!JSID_IS_ATOM(id) || JSID_TO_ATOM(id)->isIndex(&dummy))
return true;
}
layoutSize = ComputePlainObjectLayout(cx, templateShape, properties);
// The entire object must be allocatable inline.
if (UnboxedPlainObject::offsetOfData() + layoutSize > JSObject::MAX_BYTE
_SIZE)
return true;
}
UniquePtr<UnboxedLayout>& layout = enter.unboxedLayoutToCleanUp;
MOZ_ASSERT(!layout);
layout = group->zone()->make_unique<UnboxedLayout>();
if (!layout)
return false;
if (isArray) {
layout->initArray(elementType);
} else {
if (!layout->initProperties(properties, layoutSize))
return false;
// The unboxedLayouts list only tracks layouts for plain objects.
cx->compartment()->unboxedLayouts.insertFront(layout.get());
if (!SetLayoutTraceList(cx, layout.get()))
return false;
}
// We've determined that all the preliminary objects can use the new layout
// just constructed, so convert the existing group to use the unboxed class,
// and update the preliminary objects to use the new layout. Do the
// fallible stuff first before modifying any objects.
// Get an empty shape which we can use for the preliminary objects.
const Class* clasp = isArray ? &UnboxedArrayObject::class_ : &UnboxedPlainOb
ject::class_;
Shape* newShape = EmptyShape::getInitialShape(cx, clasp, group->proto(), 0);
if (!newShape) {
cx->recoverFromOutOfMemory();
return false;
}
// Accumulate a list of all the values in each preliminary object, and
// update their shapes.
Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) {
JSObject* obj = objects->get(i);
if (!obj)
continue;
bool ok;
if (isArray)
ok = GetValuesFromPreliminaryArrayObject(&obj->as<ArrayObject>(), &v
alues);
else
ok = GetValuesFromPreliminaryPlainObject(&obj->as<PlainObject>(), &v
alues);
if (!ok) {
cx->recoverFromOutOfMemory();
return false;
}
}
if (TypeNewScript* newScript = group->newScript())
layout->setNewScript(newScript);
for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) {
if (JSObject* obj = objects->get(i))
obj->as<NativeObject>().setLastPropertyMakeNonNative(newShape);
}
group->setClasp(clasp);
group->setUnboxedLayout(layout.release());
size_t valueCursor = 0;
for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) {
JSObject* obj = objects->get(i);
if (!obj)
continue;
if (isArray)
obj->as<UnboxedArrayObject>().fillAfterConvert(cx, values, &valueCur
sor);
else
obj->as<UnboxedPlainObject>().fillAfterConvert(cx, values, &valueCur
sor);
}
MOZ_ASSERT(valueCursor == values.length());
return true;
}
DefineBoxedOrUnboxedFunctor6(SetOrExtendBoxedOrUnboxedDenseElements, DefineBoxedOrUnboxedFunctor6(SetOrExtendBoxedOrUnboxedDenseElements,
ExclusiveContext*, JSObject*, uint32_t, const Value *, uint32_t, ExclusiveContext*, JSObject*, uint32_t, const Value *, uint32_t,
ShouldUpdateTypes); ShouldUpdateTypes);
DenseElementResult DenseElementResult
js::SetOrExtendAnyBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* ob j, js::SetOrExtendAnyBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* ob j,
uint32_t start, const Value* vp, u int32_t count, uint32_t start, const Value* vp, u int32_t count,
ShouldUpdateTypes updateTypes) ShouldUpdateTypes updateTypes)
{ {
SetOrExtendBoxedOrUnboxedDenseElementsFunctor functor(cx, obj, start, vp, co unt, updateTypes); SetOrExtendBoxedOrUnboxedDenseElementsFunctor functor(cx, obj, start, vp, co unt, updateTypes);
 End of changes. 4 change blocks. 
422 lines changed or deleted 0 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)