void ESTreeIRGen::emitDestructuringObject(
    bool declInit,
    ESTree::ObjectPatternNode *target,
    Value *source) {
  // Keep track of which keys have been destructured.
  llvh::SmallVector<Value *, 4> excludedItems{};

  if (target->_properties.empty() ||
      llvh::isa<ESTree::RestElementNode>(target->_properties.front())) {
    // ES10.0 13.3.3.5
    // 1. Perform ? RequireObjectCoercible(value).

    // The extremely unlikely case that the user is attempting to destructure
    // into {} or {...rest}. Any other object destructuring will fail upon
    // attempting to retrieve a real property from `source`.
    // We must check that the source can be destructured,
    // and the only time this will throw is if source is undefined or null.
    auto *throwBB = Builder.createBasicBlock(Builder.getFunction());
    auto *doneBB = Builder.createBasicBlock(Builder.getFunction());

    // Use == instead of === to account for both undefined and null.
    Builder.createCondBranchInst(
        Builder.createBinaryOperatorInst(
            source,
            Builder.getLiteralNull(),
            BinaryOperatorInst::OpKind::EqualKind),
        throwBB,
        doneBB);

    Builder.setInsertionBlock(throwBB);
    genBuiltinCall(
        BuiltinMethod::HermesBuiltin_throwTypeError,
        {source,
         Builder.getLiteralString(
             "Cannot destructure 'undefined' or 'null'.")});
    // throwTypeError will always throw.
    // This return is here to ensure well-formed IR, and will not run.
    Builder.createReturnInst(Builder.getLiteralUndefined());

    Builder.setInsertionBlock(doneBB);
  }

  for (auto &elem : target->_properties) {
    if (auto *rest = llvh::dyn_cast<ESTree::RestElementNode>(&elem)) {
      emitRestProperty(declInit, rest, excludedItems, source);
      break;
    }
    auto *propNode = cast<ESTree::PropertyNode>(&elem);

    ESTree::Node *valueNode = propNode->_value;
    ESTree::Node *init = nullptr;

    // If we have an initializer, unwrap it.
    if (auto *assign =
            llvh::dyn_cast<ESTree::AssignmentPatternNode>(valueNode)) {
      valueNode = assign->_left;
      init = assign->_right;
    }

    Identifier nameHint = llvh::isa<ESTree::IdentifierNode>(valueNode)
        ? getNameFieldFromID(valueNode)
        : Identifier{};

    if (llvh::isa<ESTree::IdentifierNode>(propNode->_key) &&
        !propNode->_computed) {
      Identifier key = getNameFieldFromID(propNode->_key);
      excludedItems.push_back(Builder.getLiteralString(key));
      auto *loadedValue = Builder.createLoadPropertyInst(source, key);
      createLRef(valueNode, declInit)
          .emitStore(emitOptionalInitialization(loadedValue, init, nameHint));
    } else {
      Value *key = genExpression(propNode->_key);
      excludedItems.push_back(key);
      auto *loadedValue = Builder.createLoadPropertyInst(source, key);
      createLRef(valueNode, declInit)
          .emitStore(emitOptionalInitialization(loadedValue, init, nameHint));
    }
  }
}
void ESTreeIRGen::emitDestructuringObject(
    bool declInit,
    ESTree::ObjectPatternNode *target,
    Value *source) {
   
  llvh::SmallVector<Value *, 4> excludedItems{};

  if (target->_properties.empty() ||
      llvh::isa<ESTree::RestElementNode>(target->_properties.front())) {
     
     

     
     
     
     
     
    auto *throwBB = Builder.createBasicBlock(Builder.getFunction());
    auto *doneBB = Builder.createBasicBlock(Builder.getFunction());

     
    Builder.createCondBranchInst(
        Builder.createBinaryOperatorInst(
            source,
            Builder.getLiteralNull(),
            BinaryOperatorInst::OpKind::EqualKind),
        throwBB,
        doneBB);

    Builder.setInsertionBlock(throwBB);
    genBuiltinCall(
        BuiltinMethod::HermesBuiltin_throwTypeError,
        {source,
         Builder.getLiteralString(
             "Cannot destructure 'undefined' or 'null'.")});
     
     
    Builder.createReturnInst(Builder.getLiteralUndefined());

    Builder.setInsertionBlock(doneBB);
  }

  for (auto &elem : target->_properties) {
    if (auto *rest = llvh::dyn_cast<ESTree::RestElementNode>(&elem)) {
      emitRestProperty(declInit, rest, excludedItems, source);
      break;
    }
    auto *propNode = cast<ESTree::PropertyNode>(&elem);

    ESTree::Node *valueNode = propNode->_value;
    ESTree::Node *init = nullptr;

     
    if (auto *assign =
            llvh::dyn_cast<ESTree::AssignmentPatternNode>(valueNode)) {
      valueNode = assign->_left;
      init = assign->_right;
    }

    Identifier nameHint = llvh::isa<ESTree::IdentifierNode>(valueNode)
        ? getNameFieldFromID(valueNode)
        : Identifier{};

    if (llvh::isa<ESTree::IdentifierNode>(propNode->_key) &&
        !propNode->_computed) {
      Identifier key = getNameFieldFromID(propNode->_key);
      excludedItems.push_back(Builder.getLiteralString(key));
      auto *loadedValue = Builder.createLoadPropertyInst(source, key);
      createLRef(valueNode, declInit)
          .emitStore(emitOptionalInitialization(loadedValue, init, nameHint));
    } else {
      Value *key = genExpression(propNode->_key);
      excludedItems.push_back(key);
      auto *loadedValue = Builder.createLoadPropertyInst(source, key);
      createLRef(valueNode, declInit)
          .emitStore(emitOptionalInitialization(loadedValue, init, nameHint));
    }
  }
}