Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions ext/sysvsem/Module.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\ext\sysvsem;

use PHPCompiler\ModuleAbstract;
use PHPCompiler\Runtime;
use PHPCompiler\VM\Context;

/**
* sysvsem extension module entry (php-src ext/sysvsem/sysvsem.c; #3704).
*/
class Module extends ModuleAbstract
{
public function init(Runtime $runtime): void
{
parent::init($runtime);
self::registerClasses($runtime->vmContext);
}

public static function registerClasses(Context $ctx): void
{
VmSem::registerClass($ctx);
}

public function getFunctions(): array
{
return [
new sem_get(),
new sem_acquire(),
new sem_release(),
new sem_remove(),
];
}
}
125 changes: 125 additions & 0 deletions ext/sysvsem/SemArgs.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\ext\sysvsem;

use PHPCompiler\Frame;
use PHPCompiler\VM\EnumCaseSupport;
use PHPCompiler\VM\ObjectEntry;
use PHPCompiler\VM\Variable;
use PHPCompiler\ext\standard\VmStreamArg;

/** Shared argument parsing for sem_* builtins (#3704). */
final class SemArgs
{
public static function requireAvailable(string $fn): void
{
if (!VmSem::available()) {
throw new \Error($fn.'() is not available in this compiler build');
}
}

public static function parseKey(Frame $frame, string $fn, int $index = 0): int
{
$var = $frame->calledArgs[$index]->resolveIndirect();
if (Variable::TYPE_INTEGER !== $var->type) {
throw new \TypeError(\sprintf(
'%s(): Argument #%d ($%s) must be of type int, %s given',
$fn,
$index + 1,
'key',
VmStreamArg::debugTypeName($var)
));
}

return $var->toInt();
}

public static function parseOptionalInt(Frame $frame, int $index, string $fn, string $param): ?int
{
if (!isset($frame->calledArgs[$index])) {
return null;
}
$var = $frame->calledArgs[$index]->resolveIndirect();
if (Variable::TYPE_NULL === $var->type) {
return null;
}
if (Variable::TYPE_INTEGER !== $var->type) {
throw new \TypeError(\sprintf(
'%s(): Argument #%d ($%s) must be of type int, %s given',
$fn,
$index + 1,
$param,
VmStreamArg::debugTypeName($var)
));
}

return $var->toInt();
}

public static function parseOptionalBool(Frame $frame, int $index, string $fn, string $param): ?bool
{
if (!isset($frame->calledArgs[$index])) {
return null;
}
$var = $frame->calledArgs[$index]->resolveIndirect();
if (Variable::TYPE_NULL === $var->type) {
return null;
}
if (Variable::TYPE_BOOL !== $var->type) {
throw new \TypeError(\sprintf(
'%s(): Argument #%d ($%s) must be of type bool, %s given',
$fn,
$index + 1,
$param,
VmStreamArg::debugTypeName($var)
));
}

return $var->toBool();
}

public static function parseSemaphore(Frame $frame, string $fn, int $index = 0): ObjectEntry
{
$var = $frame->calledArgs[$index];
$resolved = $var->resolveIndirect();
if (EnumCaseSupport::isEnumCaseVariable($resolved)) {
throw new \TypeError(\sprintf(
'%s(): Argument #%d ($semaphore) must be of type SysvSemaphore, %s given',
$fn,
$index + 1,
EnumCaseSupport::typeNameForVariable($resolved)
));
}
if (Variable::TYPE_OBJECT !== $resolved->type) {
throw new \TypeError(\sprintf(
'%s(): Argument #%d ($semaphore) must be of type SysvSemaphore, %s given',
$fn,
$index + 1,
VmStreamArg::debugTypeName($resolved)
));
}
$object = $resolved->toObject();
if (!VmSem::isSysvSemaphoreObject($object)) {
throw new \TypeError(\sprintf(
'%s(): Argument #%d ($semaphore) must be of type SysvSemaphore, %s given',
$fn,
$index + 1,
$object->class->name
));
}

return $object;
}

public static function requireHost(ObjectEntry $object, string $fn): object
{
$host = VmSem::hostForObject($object);
if (null === $host) {
throw new \ValueError($fn.'(): Argument #1 ($semaphore) has already been detached');
}

return $host;
}
}
126 changes: 126 additions & 0 deletions ext/sysvsem/VmSem.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\ext\sysvsem;

use PHPCompiler\VM\ClassEntry;
use PHPCompiler\VM\Context;
use PHPCompiler\VM\ObjectEntry;
use PHPCompiler\VM\Variable;

/**
* System V semaphores — host sysvsem delegation (php-src ext/sysvsem/sysvsem.c; #3704).
*
* PHP-in-PHP: SysvSemaphore VM objects map to host {@see \SysvSemaphore} handles.
*/
final class VmSem
{
public const CLASS_LC = 'sysvsemaphore';

/** @var array<int, object> VM object id => host SysvSemaphore */
private static array $hostByObjectId = [];

public static function available(): bool
{
return \function_exists('sem_get');
}

public static function registerClass(Context $ctx): void
{
if (isset($ctx->classes[self::CLASS_LC])) {
return;
}

$entry = new ClassEntry('SysvSemaphore');
$entry->isInternal = true;
$ctx->classes[self::CLASS_LC] = $entry;
}

public static function wrapHost(Context $ctx, object $host): Variable
{
self::registerClass($ctx);
$var = new Variable(Variable::TYPE_OBJECT);
$object = new ObjectEntry($ctx->classes[self::CLASS_LC]);
$object->constructed = true;
self::$hostByObjectId[$object->id] = $host;
$var->object($object);

return $var;
}

public static function hostForObject(ObjectEntry $object): ?object
{
if (0 !== strcasecmp($object->class->name, 'SysvSemaphore')) {
return null;
}

return self::$hostByObjectId[$object->id] ?? null;
}

public static function detachObject(ObjectEntry $object): void
{
unset(self::$hostByObjectId[$object->id]);
}

public static function isSysvSemaphoreObject(?ObjectEntry $object): bool
{
return null !== $object && 0 === strcasecmp($object->class->name, 'SysvSemaphore');
}

/**
* @return array{0: Variable|false, 1: string}
*/
public static function get(Context $ctx, int $key, ?int $maxAcquire, ?int $perm, ?int $autoRelease): array
{
if (!self::available()) {
return [false, 'sem_get() is not available in this compiler build'];
}

if (null === $maxAcquire && null === $perm && null === $autoRelease) {
$host = @\sem_get($key);
} elseif (null === $perm && null === $autoRelease) {
$host = @\sem_get($key, $maxAcquire);
} elseif (null === $autoRelease) {
$host = @\sem_get($key, $maxAcquire, $perm);
} else {
$host = @\sem_get($key, $maxAcquire, $perm, $autoRelease);
}

if (false === $host || !\is_object($host)) {
$last = \error_get_last();
$message = \is_array($last) && isset($last['message']) ? (string) $last['message'] : 'sem_get() failed';

return [false, $message];
}

return [self::wrapHost($ctx, $host), ''];
}

public static function acquire(object $host, bool $nowait = false): bool
{
if (!self::available()) {
return false;
}

return @\sem_acquire($host, $nowait);
}

public static function release(object $host): bool
{
if (!self::available()) {
return false;
}

return @\sem_release($host);
}

public static function remove(object $host): bool
{
if (!self::available()) {
return false;
}

return @\sem_remove($host);
}
}
66 changes: 66 additions & 0 deletions ext/sysvsem/sem_acquire.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\ext\sysvsem;

use PHPCompiler\Frame;
use PHPCompiler\Func\Internal;
use PHPCompiler\JIT\Context;
use PHPCompiler\JIT\Variable as JITVariable;
use PHPCompiler\VM\ErrorReporter;
use PHPLLVM\Value;

/** sem_acquire() — acquire System V semaphore (php-src ext/sysvsem/sysvsem.c; #3704). */
final class sem_acquire extends Internal
{
public function __construct()
{
parent::__construct('sem_acquire');
}

public function execute(Frame $frame): void
{
$argc = \count($frame->calledArgs);
if ($argc < 1 || $argc > 2) {
throw new \ArgumentCountError(
'sem_acquire() expects at least 1 argument, '.$argc.' given'
);
}
if (null === $frame->returnVar) {
return;
}

SemArgs::requireAvailable('sem_acquire');
$object = SemArgs::parseSemaphore($frame, 'sem_acquire');
$host = SemArgs::requireHost($object, 'sem_acquire');
$nowait = SemArgs::parseOptionalBool($frame, 1, 'sem_acquire', 'nowait') ?? false;

$result = VmSem::acquire($host, $nowait);
if (!$result) {
$this->triggerWarning($frame, 'sem_acquire() failed');
}
$frame->returnVar->bool($result);
}

public function call(Context $context, JITVariable ...$args): Value
{
throw new \LogicException(
'sem_acquire() is not supported for JIT/AOT in this compiler build (issue #3704)'
);
}

private function triggerWarning(Frame $frame, string $message): void
{
if (null === $frame->vmContext) {
return;
}
$frame->vmContext->errors->triggerError(
$message,
ErrorReporter::E_WARNING,
'' !== $frame->scriptPath ? $frame->scriptPath : null,
$frame->vmContext,
$frame
);
}
}
Loading