Bug 1147660, part 4 - Change NativeDefineProperty to use a PropertyDescriptor internally instead of a bunch of variables. This is a little ugly at first but it'll get better. r=efaust.

This commit is contained in:
Jason Orendorff 2015-03-23 14:32:29 -05:00
parent e3a6506631
commit 0bd3e81324

View File

@ -1194,10 +1194,12 @@ PurgeScopeChain(ExclusiveContext* cx, HandleObject obj, HandleId id)
*/
static inline bool
CheckAccessorRedefinition(ExclusiveContext* cx, HandleObject obj, HandleShape shape,
GetterOp getter, SetterOp setter, HandleId id, unsigned attrs)
Handle<PropertyDescriptor> desc)
{
MOZ_ASSERT(shape->isAccessorDescriptor());
if (shape->configurable() || (getter == shape->getter() && setter == shape->setter()))
if (shape->configurable())
return true;
if (desc.getter() == shape->getter() && desc.setter() == shape->setter())
return true;
/*
@ -1206,7 +1208,7 @@ CheckAccessorRedefinition(ExclusiveContext* cx, HandleObject obj, HandleShape sh
* never have such a thing on its proto chain directly on the web, so we
* should be OK optimizing access to accessors found on such an object.
*/
if ((attrs & JSPROP_REDEFINE_NONCONFIGURABLE) &&
if ((desc.attributes() & JSPROP_REDEFINE_NONCONFIGURABLE) &&
obj->is<GlobalObject>() &&
!obj->getClass()->isDOMClass())
{
@ -1216,26 +1218,19 @@ CheckAccessorRedefinition(ExclusiveContext* cx, HandleObject obj, HandleShape sh
if (!cx->isJSContext())
return false;
return Throw(cx->asJSContext(), id, JSMSG_CANT_REDEFINE_PROP);
return Throw(cx->asJSContext(), shape->propid(), JSMSG_CANT_REDEFINE_PROP);
}
bool
js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId id,
Handle<PropertyDescriptor> desc,
Handle<PropertyDescriptor> desc_,
ObjectOpResult& result)
{
desc.assertValid();
desc_.assertValid();
GetterOp getter = desc.getter();
SetterOp setter = desc.setter();
unsigned attrs = desc.attributes();
MOZ_ASSERT(!(attrs & JSPROP_PROPOP_ACCESSORS));
AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
Rooted<PropertyDescriptor> desc(cx, desc_);
RootedShape shape(cx);
RootedValue value(cx, desc.value());
// If defining a getter or setter, we must check for its counterpart and
// update the attributes and property ops. A getter or setter is really
@ -1256,24 +1251,26 @@ js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId
shape = obj->lookup(cx, id);
}
if (shape->isAccessorDescriptor()) {
if (!CheckAccessorRedefinition(cx, obj, shape, getter, setter, id, attrs))
if (!CheckAccessorRedefinition(cx, obj, shape, desc))
return false;
attrs = ApplyOrDefaultAttributes(attrs, shape);
shape = NativeObject::changeProperty(cx, obj, shape,
attrs | JSPROP_GETTER | JSPROP_SETTER,
(attrs & JSPROP_GETTER)
? getter
: shape->getter(),
(attrs & JSPROP_SETTER)
? setter
: shape->setter());
desc.setAttributes(ApplyOrDefaultAttributes(desc.attributes(), shape));
if (!desc.hasGetterObject())
desc.setGetter(shape->getter());
if (!desc.hasSetterObject())
desc.setSetter(shape->setter());
desc.attributesRef() |= JSPROP_GETTER | JSPROP_SETTER;
desc.assertComplete();
shape = NativeObject::changeProperty(cx, obj, shape, desc.attributes(),
desc.getter(), desc.setter());
if (!shape)
return false;
if (!PurgeScopeChain(cx, obj, id))
return false;
JS_ALWAYS_TRUE(UpdateShapeTypeAndValue(cx, obj, shape, value));
if (!CallAddPropertyHook(cx, obj, shape, value))
JS_ALWAYS_TRUE(UpdateShapeTypeAndValue(cx, obj, shape, desc.value()));
if (!CallAddPropertyHook(cx, obj, shape, desc.value()))
return false;
return result.succeed();
}
@ -1282,7 +1279,7 @@ js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId
// Either we are converting a data property to an accessor property, or
// creating a new accessor property; either way [[Get]] and [[Set]]
// must both be filled in.
attrs |= JSPROP_GETTER | JSPROP_SETTER;
desc.attributesRef() |= JSPROP_GETTER | JSPROP_SETTER;
} else if (desc.hasValue()) {
// If we did a normal lookup here, it would cause resolve hook recursion in
// the following case. Suppose the first script we run in a lazy global is
@ -1302,13 +1299,14 @@ js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId
// If any other JSPROP_IGNORE_* attributes are present, copy the
// corresponding JSPROP_* attributes from the existing property.
if (IsImplicitDenseOrTypedArrayElement(shape)) {
attrs = ApplyAttributes(attrs, true, true, !IsAnyTypedArray(obj));
desc.setAttributes(ApplyAttributes(desc.attributes(), true, true,
!IsAnyTypedArray(obj)));
} else {
attrs = ApplyOrDefaultAttributes(attrs, shape);
desc.setAttributes(ApplyOrDefaultAttributes(desc.attributes(), shape));
// Do not redefine a nonconfigurable accessor property.
if (shape->isAccessorDescriptor()) {
if (!CheckAccessorRedefinition(cx, obj, shape, getter, setter, id, attrs))
if (!CheckAccessorRedefinition(cx, obj, shape, desc))
return false;
}
}
@ -1333,28 +1331,29 @@ js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId
}
if (shape->isAccessorDescriptor() &&
!CheckAccessorRedefinition(cx, obj, shape, getter, setter, id, attrs))
!CheckAccessorRedefinition(cx, obj, shape, desc))
{
return false;
}
attrs = ApplyOrDefaultAttributes(attrs, shape);
desc.setAttributes(ApplyOrDefaultAttributes(desc.attributes(), shape));
if (shape->isAccessorDescriptor() && !(attrs & JSPROP_IGNORE_READONLY)) {
if (shape->isAccessorDescriptor() && desc.hasWritable()) {
// ES6 draft 2014-10-14 9.1.6.3 step 7.c: Since [[Writable]]
// is present, change the existing accessor property to a data
// property.
value = UndefinedValue();
desc.value().setUndefined();
} else {
// We are at most changing some attributes, and cannot convert
// from data descriptor to accessor, or vice versa. Take
// everything from the shape that we aren't changing.
uint32_t propMask = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT;
attrs = (shape->attributes() & ~propMask) | (attrs & propMask);
getter = shape->getter();
setter = shape->setter();
desc.setAttributes((shape->attributes() & ~propMask) |
(desc.attributes() & propMask));
desc.setGetter(shape->getter());
desc.setSetter(shape->setter());
if (shape->hasSlot())
value = obj->getSlot(shape->slot());
desc.value().set(obj->getSlot(shape->slot()));
}
}
}
@ -1363,14 +1362,15 @@ js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId
// needed to be copied from an existing property have been copied by
// now. Since we can never get here with JSPROP_IGNORE_VALUE relevant, just
// clear it.
attrs = ApplyOrDefaultAttributes(attrs) & ~JSPROP_IGNORE_VALUE;
desc.setAttributes(ApplyOrDefaultAttributes(desc.attributes()) & ~JSPROP_IGNORE_VALUE);
if (obj->is<ArrayObject>()) {
Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
if (id == NameToId(cx->names().length)) {
if (!cx->shouldBeJSContext())
return false;
return ArraySetLength(cx->asJSContext(), arr, id, attrs, value, result);
return ArraySetLength(cx->asJSContext(), arr, id, desc.attributes(), desc.value(),
result);
}
uint32_t index;
@ -1388,17 +1388,18 @@ js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId
// At this point, no mutation has happened yet, but all ES6 error cases
// have been dealt with.
MOZ_ASSERT(getter != JS_PropertyStub);
MOZ_ASSERT(setter != JS_StrictPropertyStub);
desc.assertComplete();
MOZ_ASSERT(desc.getter() != JS_PropertyStub);
MOZ_ASSERT(desc.setter() != JS_StrictPropertyStub);
if (!PurgeScopeChain(cx, obj, id))
return false;
// Use dense storage for new indexed properties where possible.
if (JSID_IS_INT(id) &&
!getter &&
!setter &&
attrs == JSPROP_ENUMERATE &&
!desc.getter() &&
!desc.setter() &&
desc.attributes() == JSPROP_ENUMERATE &&
(!obj->isIndexed() || !obj->containsPure(id)) &&
!IsAnyTypedArray(obj))
{
@ -1407,19 +1408,19 @@ js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId
if (edResult == NativeObject::ED_FAILED)
return false;
if (edResult == NativeObject::ED_OK) {
obj->setDenseElementWithType(cx, index, value);
if (!CallAddPropertyHookDense(cx, obj, index, value))
obj->setDenseElementWithType(cx, index, desc.value());
if (!CallAddPropertyHookDense(cx, obj, index, desc.value()))
return false;
return result.succeed();
}
}
AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
shape = NativeObject::putProperty(cx, obj, id, getter, setter, SHAPE_INVALID_SLOT, attrs, 0);
shape = NativeObject::putProperty(cx, obj, id, desc.getter(), desc.setter(),
SHAPE_INVALID_SLOT, desc.attributes(), 0);
if (!shape)
return false;
if (!UpdateShapeTypeAndValue(cx, obj, shape, value))
if (!UpdateShapeTypeAndValue(cx, obj, shape, desc.value()))
return false;
// Clear any existing dense index after adding a sparse indexed property,
@ -1430,24 +1431,24 @@ js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId
uint32_t index = JSID_TO_INT(id);
NativeObject::removeDenseElementForSparseIndex(cx, obj, index);
NativeObject::EnsureDenseResult edResult = NativeObject::maybeDensifySparseElements(cx, obj);
NativeObject::EnsureDenseResult edResult =
NativeObject::maybeDensifySparseElements(cx, obj);
if (edResult == NativeObject::ED_FAILED)
return false;
if (edResult == NativeObject::ED_OK) {
MOZ_ASSERT(!setter);
if (!CallAddPropertyHookDense(cx, obj, index, value))
MOZ_ASSERT(!desc.setter());
if (!CallAddPropertyHookDense(cx, obj, index, desc.value()))
return false;
return result.succeed();
}
}
if (!CallAddPropertyHook(cx, obj, shape, value))
if (!CallAddPropertyHook(cx, obj, shape, desc.value()))
return false;
return result.succeed();
}
bool
js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId id,
HandleValue value, GetterOp getter, SetterOp setter, unsigned attrs,