GraphQL-Houtou

 view release on metacpan or  search on metacpan

lib/GraphQL/Houtou/Runtime/NativeRuntime.pm  view on Meta::CPAN

        $native_program,
        $root_value,
        $context_value,
        $variables || {},
      );
    }
    my $prepared_variables = GraphQL::Houtou::Runtime::InputCoercion::prepare_variables(
      $self->runtime_schema,
      $native_program,
      $variables || {},
    );
    my $specialized = $self->_cached_specialized_program(
      $native_program,
      $prepared_variables,
    );
    return $self->execute_compact_program($specialized, %opts, variables => $prepared_variables);
  }
  if (!$has_root_value && !$has_context_value && !$has_variables) {
    return GraphQL::Houtou::XS::VM::execute_native_program_auto_simple_xs(
      $runtime_handle,
      $native_program,
    );
  }
  return GraphQL::Houtou::XS::VM::execute_native_program_auto_xs(
    $runtime_handle,
    $native_program,
    $root_value,
    $context_value,
    $variables,
  );
}

sub execute_compact_program {
  my ($self, $program, %opts) = @_;
  my $native_program = _require_native_program($program);
  return GraphQL::Houtou::XS::VM::execute_native_program_handle_xs(
    $self->_native_runtime_handle,
    $native_program,
    $opts{root_value},
    $opts{context},
    $opts{variables},
  );
}

# Drive a possibly-pending async result to completion using the caller's
# on_stall callback. This is the core batching contract: execution runs
# until every field either completed or is waiting on a promise ("stall"),
# then on_stall is invoked and must make progress (return a true dispatch
# count) by resolving promises - typically by flushing data loaders. Each
# resolution runs its continuations synchronously, advancing the scheduler
# to the next stall. No progress while promises remain pending is reported
# as a deadlock.
sub _settle_result {
  my ($self, $result, $on_stall) = @_;
  return $result
    if !GraphQL::Houtou::XS::VM::runtime_value_is_async_xs(
      $self->_native_runtime_handle, $result,
    );

  my ($settled, $value) = (0, undef);
  # Keep the derived chain alive until settlement; Future-style adapters may
  # otherwise discard it before its callbacks run.
  my $chain;
  eval {
    $chain = GraphQL::Houtou::XS::VM::runtime_then_async_xs(
      $self->_native_runtime_handle,
      $result,
      sub { ($settled, $value) = (1, $_[0]) },
      sub { ($settled, $value) = (-1, $_[0]) },
    );
    1;
  } or do {
    my $error = $@;
    GraphQL::Houtou::XS::VM::cancel_pending_response_xs($result);
    die $error;
  };
  while (!$settled) {
    my $progressed = $on_stall->();
    if (!$settled && !$progressed) {
      # The request is being abandoned while promises are pending; cancel
      # it so the pending machinery (a reference cycle while armed) is
      # torn down instead of leaking with the abandoned frames.
      GraphQL::Houtou::XS::VM::cancel_pending_response_xs($result);
      die "GraphQL execution stalled: promises are pending but on_stall made no progress"
        . " (a resolver returned a promise that no registered loader will resolve)\n";
    }
  }
  if ($settled < 0) {
    die $value if ref $value || ($value // '') ne '';
    die "GraphQL execution failed with an unknown async rejection\n";
  }
  return $value;
}

sub execute_bundle_descriptor {
  my ($self, $descriptor, %opts) = @_;
  my $bundle = $self->load_bundle_descriptor($descriptor);
  return $self->execute_bundle($bundle, %opts);
}

# Request-stage checks shared by execute_document and its JSON sibling:
# depth limit and full query validation. The depth check is skipped for
# documents already in the program cache; validation is skipped only for
# documents this runtime has actually validated before (a cache entry
# stored under validate => 0 proves nothing). Returns an arrayref of
# request errors, or undef when execution may proceed. Request errors
# produce an errors-only envelope (no "data" key), per the spec's
# request-error contract.
sub _document_request_errors {
  my ($self, $document, $max_depth, $max_nodes, $max_cost,
      $default_list_size, $validate, $operation_name, $allow_introspection) = @_;
  return undef if !defined $max_depth && !defined $max_nodes
    && !defined $max_cost && !$validate && $allow_introspection;

  my $is_string = !ref($document);
  my $cache_key = $is_string
    ? _document_cache_key($document, $operation_name)
    : undef;
  my $already_validated = $is_string && $self->{_validated_documents}{$cache_key};
  my $already_limited = $is_string && $self->{_limit_signatures}{$cache_key}
    && $self->{_limit_signatures}{$cache_key} eq _limit_signature(



( run in 2.638 seconds using v1.01-cache-2.11-cpan-14f38c9f855 )