in lib/VM/JSProxy.cpp [137:237]
ExecutionStatus isCompatiblePropertyDescriptor(
Runtime &runtime,
const DefinePropertyFlags &desc,
Handle<> descValueOrAccessor,
const ComputedPropertyDescriptor ¤t,
Handle<> currentValueOrAccessor) {
// 4. If current.[[Configurable]] is false, then
if (!current.flags.configurable) {
// a. If Desc.[[Configurable]] is present and its value is true, return
// false.
if (desc.setConfigurable && desc.configurable) {
return runtime.raiseTypeError(
"trap result is configurable but target property is non-configurable");
}
// b. If Desc.[[Enumerable]] is present and the [[Enumerable]]
// fields of current and Desc are the Boolean negation of each
// other, return false.
if (desc.setEnumerable && desc.enumerable != current.flags.enumerable) {
return runtime.raiseTypeError(
TwineChar16("trap result is ") + (desc.enumerable ? "" : "not ") +
"enumerable but target property is " +
(current.flags.enumerable ? "" : "not ") + "enumerable");
}
}
// 5. If IsGenericDescriptor(Desc) is true, no further validation is required.
bool descIsAccessor = desc.setSetter || desc.setGetter;
bool descIsData = desc.setValue || desc.setWritable;
assert(
(!descIsData || !descIsAccessor) &&
"descriptor cannot be both Data and Accessor");
if (!descIsData && !descIsAccessor) {
return ExecutionStatus::RETURNED;
}
// 6. Else if IsDataDescriptor(current) and IsDataDescriptor(Desc)
// have different results, then
bool currentIsAccessor = current.flags.accessor;
bool currentIsData = !currentIsAccessor;
if (currentIsData != descIsData) {
// a. If current.[[Configurable]] is false, return false.
if (!current.flags.configurable) {
return runtime.raiseTypeError(
TwineChar16("trap result is ") +
(currentIsData ? "data " : "accessor ") + "but target property is " +
(descIsData ? "data " : "accessor ") + "and non-configurable");
}
}
// 7. Else if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both
// true, then
// a. If current.[[Configurable]] is false and current.[[Writable]] is
// false, then
if (currentIsData && descIsData && !current.flags.configurable &&
!current.flags.writable) {
// i. If Desc.[[Writable]] is present and Desc.[[Writable]] is true,
// return false.
if (desc.setWritable && desc.writable) {
return runtime.raiseTypeError(
"trap result is writable but "
"target property is non-configurable and non-writable");
}
// ii. If Desc.[[Value]] is present and SameValue(Desc.[[Value]],
// current.[[Value]]) is false, return false.
if (desc.setValue &&
!isSameValue(
descValueOrAccessor.getHermesValue(),
currentValueOrAccessor.getHermesValue())) {
return runtime.raiseTypeError(
"trap result has different value than target property but "
"target property is non-configurable and non-writable");
}
// iii. Return true.
return ExecutionStatus::RETURNED;
}
// 8. Else IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are
// both true,
// a. If current.[[Configurable]] is false, then
if (currentIsAccessor && descIsAccessor && !current.flags.configurable) {
PropertyAccessor *descAccessor =
vmcast<PropertyAccessor>(descValueOrAccessor.get());
PropertyAccessor *currentAccessor =
vmcast<PropertyAccessor>(currentValueOrAccessor.get());
// i. If Desc.[[Set]] is present and SameValue(Desc.[[Set]],
// current.[[Set]]) is false, return false.
if (descAccessor->setter &&
descAccessor->setter != currentAccessor->setter) {
return runtime.raiseTypeError(
"trap result has different setter than target property but "
"target property is non-configurable");
}
// ii. If Desc.[[Get]] is present and SameValue(Desc.[[Get]],
// current.[[Get]]) is false, return false.
if (descAccessor->getter &&
descAccessor->getter != currentAccessor->getter) {
return runtime.raiseTypeError(
"trap result has different getter than target property but "
"target property is non-configurable");
}
// iii. Return true.
}
return ExecutionStatus::RETURNED;
}