ExecutionStatus isCompatiblePropertyDescriptor()

in lib/VM/JSProxy.cpp [137:237]


ExecutionStatus isCompatiblePropertyDescriptor(
    Runtime &runtime,
    const DefinePropertyFlags &desc,
    Handle<> descValueOrAccessor,
    const ComputedPropertyDescriptor &current,
    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;
}