JSObject *create(JSContext *cx, HandleObject self, Mode mode, HandleObject owner) {
  JS_SetReservedSlot(self, Slots::Mode, JS::Int32Value(static_cast<int32_t>(mode)));
  uint32_t handle = UINT32_MAX - 1;
  if (mode != Mode::Standalone)
    handle = RequestOrResponse::handle(owner);
  JS_SetReservedSlot(self, Slots::Handle, JS::Int32Value(static_cast<int32_t>(handle)));

  RootedObject backing_map(cx, JS::NewMapObject(cx));
  if (!backing_map)
    return nullptr;
  JS::SetReservedSlot(self, Slots::BackingMap, JS::ObjectValue(*backing_map));

  bool lazy = false;
  if ((mode == Mode::ProxyToRequest && Request::is_downstream(owner)) ||
      (mode == Mode::ProxyToResponse && Response::is_upstream(owner))) {
    lazy = true;
    if (!detail::get_header_names_from_handle(cx, handle, mode, backing_map))
      return nullptr;
  }

  JS_SetReservedSlot(self, Slots::HasLazyValues, JS::BooleanValue(lazy));

  return self;
}
JSObject *create(JSContext *cx, HandleObject self, Mode mode, HandleObject owner) {
  JS_SetReservedSlot(self, Slots::Mode, JS::Int32Value(static_cast<int32_t>(mode)));
  uint32_t handle = UINT32_MAX - 1;
  if (mode != Mode::Standalone)
    handle = RequestOrResponse::handle(owner);
  JS_SetReservedSlot(self, Slots::Handle, JS::Int32Value(static_cast<int32_t>(handle)));

  RootedObject backing_map(cx, JS::NewMapObject(cx));
  if (!backing_map)
    return nullptr;
  JS::SetReservedSlot(self, Slots::BackingMap, JS::ObjectValue(*backing_map));

  bool lazy = false;
  if ((mode == Mode::ProxyToRequest && Request::is_downstream(owner)) ||
      (mode == Mode::ProxyToResponse && Response::is_upstream(owner))) {
    lazy = true;
    if (!detail::get_header_names_from_handle(cx, handle, mode, backing_map))
      return nullptr;
  }

  JS_SetReservedSlot(self, Slots::HasLazyValues, JS::BooleanValue(lazy));

  return self;
}