aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe-php-rpc
diff options
context:
space:
mode:
Diffstat (limited to 'crates/shirabe-php-rpc')
-rw-r--r--crates/shirabe-php-rpc/php/guards/Composer/Util/ProcessExecutor.php114
-rw-r--r--crates/shirabe-php-rpc/php/stubs/Composer/Util/Filesystem.php32
-rw-r--r--crates/shirabe-php-rpc/php/stubs/Composer/Util/ProcessExecutor.php176
-rw-r--r--crates/shirabe-php-rpc/php/worker.php23
-rw-r--r--crates/shirabe-php-rpc/src/lib.rs24
5 files changed, 227 insertions, 142 deletions
diff --git a/crates/shirabe-php-rpc/php/guards/Composer/Util/ProcessExecutor.php b/crates/shirabe-php-rpc/php/guards/Composer/Util/ProcessExecutor.php
deleted file mode 100644
index 405905e8..00000000
--- a/crates/shirabe-php-rpc/php/guards/Composer/Util/ProcessExecutor.php
+++ /dev/null
@@ -1,114 +0,0 @@
-<?php
-
-// Generated by scripts/plugin-stub-generator; do not edit by hand.
-// Guard for Composer\Util\ProcessExecutor.
-// The Rust side owns this class and the worker has no proxy for it, so this
-// declaration shadows the real one: the constants and the hierarchy stay, while
-// constructing it or calling anything on it raises an explicit error.
-
-namespace Composer\Util;
-
-use Composer\IO\IOInterface;
-use React\Promise\PromiseInterface;
-
-class ProcessExecutor
-{
- private const STATUS_QUEUED = 1;
- private const STATUS_STARTED = 2;
- private const STATUS_COMPLETED = 3;
- private const STATUS_FAILED = 4;
- private const STATUS_ABORTED = 5;
- private const BUILTIN_CMD_COMMANDS = [
- 'assoc', 'break', 'call', 'cd', 'chdir', 'cls', 'color', 'copy', 'date',
- 'del', 'dir', 'echo', 'endlocal', 'erase', 'exit', 'for', 'ftype', 'goto',
- 'help', 'if', 'label', 'md', 'mkdir', 'mklink', 'move', 'path', 'pause',
- 'popd', 'prompt', 'pushd', 'rd', 'rem', 'ren', 'rename', 'rmdir', 'set',
- 'setlocal', 'shift', 'start', 'time', 'title', 'type', 'ver', 'vol',
- ];
- private const GIT_CMDS_NEED_GIT_DIR = [
- ['show'],
- ['log'],
- ['branch'],
- ['remote', 'set-url'],
- ];
-
- public function __construct(?IOInterface $io = null)
- {
- \ShirabeUnsupportedClass::fail(self::class, '__construct');
- }
-
- public function execute($command, &$output = null, ?string $cwd = null): int
- {
- \ShirabeUnsupportedClass::fail(self::class, 'execute');
- }
-
- public function executeTty($command, ?string $cwd = null): int
- {
- \ShirabeUnsupportedClass::fail(self::class, 'executeTty');
- }
-
- public function executeAsync($command, ?string $cwd = null): PromiseInterface
- {
- \ShirabeUnsupportedClass::fail(self::class, 'executeAsync');
- }
-
- protected function outputHandler(string $type, string $buffer): void
- {
- \ShirabeUnsupportedClass::fail(self::class, 'outputHandler');
- }
-
- public function setMaxJobs(int $maxJobs): void
- {
- \ShirabeUnsupportedClass::fail(self::class, 'setMaxJobs');
- }
-
- public function resetMaxJobs(): void
- {
- \ShirabeUnsupportedClass::fail(self::class, 'resetMaxJobs');
- }
-
- public function wait($index = null): void
- {
- \ShirabeUnsupportedClass::fail(self::class, 'wait');
- }
-
- public function enableAsync(): void
- {
- \ShirabeUnsupportedClass::fail(self::class, 'enableAsync');
- }
-
- public function countActiveJobs($index = null): int
- {
- \ShirabeUnsupportedClass::fail(self::class, 'countActiveJobs');
- }
-
- public function splitLines(?string $output): array
- {
- \ShirabeUnsupportedClass::fail(self::class, 'splitLines');
- }
-
- public function getErrorOutput(): string
- {
- \ShirabeUnsupportedClass::fail(self::class, 'getErrorOutput');
- }
-
- public static function getTimeout(): int
- {
- \ShirabeUnsupportedClass::fail(self::class, 'getTimeout');
- }
-
- public static function setTimeout(int $timeout): void
- {
- \ShirabeUnsupportedClass::fail(self::class, 'setTimeout');
- }
-
- public static function escape($argument): string
- {
- \ShirabeUnsupportedClass::fail(self::class, 'escape');
- }
-
- public function requiresGitDirEnv($command): bool
- {
- \ShirabeUnsupportedClass::fail(self::class, 'requiresGitDirEnv');
- }
-}
diff --git a/crates/shirabe-php-rpc/php/stubs/Composer/Util/Filesystem.php b/crates/shirabe-php-rpc/php/stubs/Composer/Util/Filesystem.php
index e1532889..c66bcf56 100644
--- a/crates/shirabe-php-rpc/php/stubs/Composer/Util/Filesystem.php
+++ b/crates/shirabe-php-rpc/php/stubs/Composer/Util/Filesystem.php
@@ -83,26 +83,6 @@ class Filesystem implements \ShirabeRustStub
return $path;
}
- public static function isLocalPath(string $path)
- {
- // on windows, \\foo indicates network paths so we exclude those from local paths, however it is unsafe
- // on linux as file:////foo (which would be a network path \\foo on windows) will resolve to /foo which could be a local path
- if (Platform::isWindows()) {
- return Preg::isMatch('{^(file://(?!//)|/(?!/)|/?[a-z]:[\\\\/]|\.\.[\\\\/]|[a-z0-9_.-]+[\\\\/])}i', $path);
- }
-
- return Preg::isMatch('{^(file://|/|/?[a-z]:[\\\\/]|\.\.[\\\\/]|[a-z0-9_.-]+[\\\\/])}i', $path);
- }
-
- public static function getPlatformPath(string $path)
- {
- if (Platform::isWindows()) {
- $path = Preg::replace('{^(?:file:///([a-z]):?/)}i', 'file://$1:/', $path);
- }
-
- return Preg::replace('{^file://}i', '', $path);
- }
-
public static function isReadable(string $path)
{
if (is_readable($path)) {
@@ -121,6 +101,18 @@ class Filesystem implements \ShirabeRustStub
return false;
}
+ // Forwarded rather than materialized: it references Composer\Util\Platform, which a guard shadows in the worker.
+ public static function isLocalPath(string $path)
+ {
+ return \ShirabeRpcRuntime::callRust(0, '__shirabeCallStatic', [self::class, 'isLocalPath', [$path]]);
+ }
+
+ // Forwarded rather than materialized: it references Composer\Util\Platform, which a guard shadows in the worker.
+ public static function getPlatformPath(string $path)
+ {
+ return \ShirabeRpcRuntime::callRust(0, '__shirabeCallStatic', [self::class, 'getPlatformPath', [$path]]);
+ }
+
public function remove(string $file)
{
return \ShirabeRpcRuntime::callRust($this->__rhandle, 'remove', [$file]);
diff --git a/crates/shirabe-php-rpc/php/stubs/Composer/Util/ProcessExecutor.php b/crates/shirabe-php-rpc/php/stubs/Composer/Util/ProcessExecutor.php
new file mode 100644
index 00000000..dfa1f0d2
--- /dev/null
+++ b/crates/shirabe-php-rpc/php/stubs/Composer/Util/ProcessExecutor.php
@@ -0,0 +1,176 @@
+<?php
+
+// Generated by scripts/plugin-stub-generator; do not edit by hand.
+// Proxy stub for Composer\Util\ProcessExecutor: the public surface forwards to the Rust-side entity over RPC.
+
+namespace Composer\Util;
+
+use Composer\IO\IOInterface;
+use React\Promise\PromiseInterface;
+
+class ProcessExecutor implements \ShirabeRustStub
+{
+ /** @var int */
+ protected $__rhandle;
+ /** @var int */
+ protected $__epoch;
+
+ /**
+ * Binds a stub the registry built for an existing entity. Proxy instantiation bypasses
+ * the constructor, which belongs to plugin code building a new entity instead.
+ */
+ public function __shirabeBind(int $rhandle, int $epoch): void
+ {
+ $this->__rhandle = $rhandle;
+ $this->__epoch = $epoch;
+ }
+
+ public function __destruct()
+ {
+ \ShirabeRustObjectRegistry::release($this->__rhandle);
+ }
+
+ public function __shirabeRustHandleDescriptor(): array
+ {
+ return [
+ '__rhandle' => $this->__rhandle,
+ '__class' => static::class,
+ '__epoch' => $this->__epoch,
+ ];
+ }
+
+ public function __clone()
+ {
+ // PHP has already shallow-copied this stub, so both copies would point at one
+ // entity and release it twice. The Rust side clones the entity instead, applying
+ // whatever __clone semantics the real class defines, and this copy rebinds to the
+ // fresh handle. Entities without clone semantics answer with an explicit error.
+ [$this->__rhandle, $this->__epoch] = \ShirabeRpcRuntime::callRust($this->__rhandle, '__shirabeClone', []);
+ \ShirabeRustObjectRegistry::adopt($this->__rhandle, $this);
+ }
+
+ public function __get($name)
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, '__get', [$name]);
+ }
+
+ public function __set($name, $value): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, '__set', [$name, $value]);
+ }
+
+ public function __isset($name): bool
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, '__isset', [$name]);
+ }
+
+ public function __unset($name): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, '__unset', [$name]);
+ }
+
+ public function __construct(?IOInterface $io = null)
+ {
+ [$this->__rhandle, $this->__epoch] = \ShirabeRpcRuntime::callRust(0, '__shirabeConstruct', [static::class, [$io]]);
+ \ShirabeRustObjectRegistry::adopt($this->__rhandle, $this);
+ }
+
+ private const STATUS_QUEUED = 1;
+ private const STATUS_STARTED = 2;
+ private const STATUS_COMPLETED = 3;
+ private const STATUS_FAILED = 4;
+ private const STATUS_ABORTED = 5;
+ private const BUILTIN_CMD_COMMANDS = [
+ 'assoc', 'break', 'call', 'cd', 'chdir', 'cls', 'color', 'copy', 'date',
+ 'del', 'dir', 'echo', 'endlocal', 'erase', 'exit', 'for', 'ftype', 'goto',
+ 'help', 'if', 'label', 'md', 'mkdir', 'mklink', 'move', 'path', 'pause',
+ 'popd', 'prompt', 'pushd', 'rd', 'rem', 'ren', 'rename', 'rmdir', 'set',
+ 'setlocal', 'shift', 'start', 'time', 'title', 'type', 'ver', 'vol',
+ ];
+ private const GIT_CMDS_NEED_GIT_DIR = [
+ ['show'],
+ ['log'],
+ ['branch'],
+ ['remote', 'set-url'],
+ ];
+
+ // Forwarded rather than materialized: it reads the static property $timeout, whose value the Rust side owns.
+ public static function getTimeout(): int
+ {
+ return \ShirabeRpcRuntime::callRust(0, '__shirabeCallStatic', [self::class, 'getTimeout', []]);
+ }
+
+ // Forwarded rather than materialized: it reads the static property $timeout, whose value the Rust side owns.
+ public static function setTimeout(int $timeout): void
+ {
+ \ShirabeRpcRuntime::callRust(0, '__shirabeCallStatic', [self::class, 'setTimeout', [$timeout]]);
+ }
+
+ // Forwarded rather than materialized: it references Composer\Util\Platform, which a guard shadows in the worker.
+ public static function escape($argument): string
+ {
+ return \ShirabeRpcRuntime::callRust(0, '__shirabeCallStatic', [self::class, 'escape', [$argument]]);
+ }
+
+ public function execute($command, &$output = null, ?string $cwd = null): int
+ {
+ $__args = [$command, $output, $cwd];
+ array_splice($__args, func_num_args());
+ $__out = [];
+ $__result = \ShirabeRpcRuntime::callRust($this->__rhandle, 'execute', $__args, [1], $__out);
+ if (array_key_exists(1, $__out)) {
+ $output = $__out[1];
+ }
+ return $__result;
+ }
+
+ public function executeTty($command, ?string $cwd = null): int
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'executeTty', [$command, $cwd]);
+ }
+
+ public function executeAsync($command, ?string $cwd = null): PromiseInterface
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'executeAsync', [$command, $cwd]);
+ }
+
+ public function setMaxJobs(int $maxJobs): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setMaxJobs', [$maxJobs]);
+ }
+
+ public function resetMaxJobs(): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'resetMaxJobs', []);
+ }
+
+ public function wait($index = null): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'wait', [$index]);
+ }
+
+ public function enableAsync(): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'enableAsync', []);
+ }
+
+ public function countActiveJobs($index = null): int
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'countActiveJobs', [$index]);
+ }
+
+ public function splitLines(?string $output): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'splitLines', [$output]);
+ }
+
+ public function getErrorOutput(): string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getErrorOutput', []);
+ }
+
+ public function requiresGitDirEnv($command): bool
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'requiresGitDirEnv', [$command]);
+ }
+}
diff --git a/crates/shirabe-php-rpc/php/worker.php b/crates/shirabe-php-rpc/php/worker.php
index 6ed5df40..bc10c2de 100644
--- a/crates/shirabe-php-rpc/php/worker.php
+++ b/crates/shirabe-php-rpc/php/worker.php
@@ -295,15 +295,29 @@ final class ShirabeRpcRuntime
return array_map([self::class, 'fromWire'], $value);
}
- /** Sends a CallRustMethod request and drives the cooperative loop until its Return. */
- public static function callRust(int $rhandle, string $method, array $args)
- {
+ /**
+ * Sends a CallRustMethod request and drives the cooperative loop until its Return.
+ *
+ * `$outParamPositions` names the by-ref parameters of the called method; the Rust side
+ * answers with the value each of them holds afterwards, and the caller (a generated stub)
+ * assigns those back to its own by-ref parameters.
+ *
+ * @param list<int> $outParamPositions
+ * @param array<int, mixed>|null $outParams
+ */
+ public static function callRust(
+ int $rhandle,
+ string $method,
+ array $args,
+ array $outParamPositions = [],
+ ?array &$outParams = null
+ ) {
$corrId = self::$nextCorrId;
self::$nextCorrId += 2;
self::writeFrame(
SHIRABE_TAG_CALL_RUST_METHOD,
$corrId,
- serialize([$rhandle, $method, self::toWire($args), []])
+ serialize([$rhandle, $method, self::toWire($args), $outParamPositions])
);
while (true) {
$frame = self::readFrame();
@@ -320,6 +334,7 @@ final class ShirabeRpcRuntime
self::fail('protocol violation: unparseable response payload');
}
if ($tag === SHIRABE_TAG_RETURN) {
+ $outParams = self::fromWire($fields[1] ?? []);
return self::fromWire($fields[0]);
}
[$class, $message, $code] = $fields;
diff --git a/crates/shirabe-php-rpc/src/lib.rs b/crates/shirabe-php-rpc/src/lib.rs
index b2bc2c28..dfd8fa67 100644
--- a/crates/shirabe-php-rpc/src/lib.rs
+++ b/crates/shirabe-php-rpc/src/lib.rs
@@ -643,6 +643,12 @@ impl std::error::Error for PhpThrow {}
/// Handles `CallRustMethod` requests arriving while a Rust-initiated call is waiting for its
/// `Return` (the cooperative reentrancy loop). A handler may itself issue nested RPC calls.
+///
+/// `out_param_positions` names the by-ref parameters of the called method, as the calling stub
+/// declared them. A handler that assigns to one writes the resulting value into `out_params` under
+/// the same position, and the stub copies it back into the caller's variable; leaving a position
+/// out means the method never assigned to it, which is what PHP does with an untouched by-ref
+/// parameter.
pub trait RustMethodDispatcher {
fn dispatch(
&mut self,
@@ -650,6 +656,7 @@ pub trait RustMethodDispatcher {
method_name: &str,
args: Vec<PluginValue>,
out_param_positions: &[u32],
+ out_params: &mut IndexMap<u32, PluginValue>,
) -> Result<PluginValue, PhpThrow>;
}
@@ -812,10 +819,15 @@ fn rpc_call(
args,
out_param_positions,
} => {
+ let mut out_params = IndexMap::new();
let outcome = match dispatcher.as_deref_mut() {
- Some(dispatcher) => {
- dispatcher.dispatch(rhandle, &method_name, args, &out_param_positions)
- }
+ Some(dispatcher) => dispatcher.dispatch(
+ rhandle,
+ &method_name,
+ args,
+ &out_param_positions,
+ &mut out_params,
+ ),
// Never fall back to a silent null: an unroutable callback is reported as an
// explicit error on the PHP side.
None => Err(PhpThrow::runtime(format!(
@@ -827,7 +839,7 @@ fn rpc_call(
Ok(value) => Frame::Return {
corr_id,
value,
- out_params: IndexMap::new(),
+ out_params,
},
Err(throw) => Frame::Throw {
corr_id,
@@ -1011,6 +1023,10 @@ const STUB_FILES: &[(&str, &str)] = &[
"Composer/Util/Filesystem.php",
include_str!("../php/stubs/Composer/Util/Filesystem.php"),
),
+ (
+ "Composer/Util/ProcessExecutor.php",
+ include_str!("../php/stubs/Composer/Util/ProcessExecutor.php"),
+ ),
];
/// Hand-written worker-side classes (two-world implementations with behavior of their own, not