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
1 change: 1 addition & 0 deletions docs/capabilities-syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Tracking issues: [#58](https://github.com/PurHur/php-compiler/issues/58), [#145]
| `__serialize` / `__unserialize` magic methods | no | no | yes | [#1365](https://github.com/PurHur/php-compiler/issues/1365) | serialize()/unserialize() call __serialize/__unserialize when present; VM via VmSerialize |
| Multi-type catch `catch (A|B $e)` | yes | yes | yes | [#1362](https://github.com/PurHur/php-compiler/issues/1362) | php-cfg records union types per catch; VM filters TYPE_CATCH via OpCode.catchTypes; compliance PHPT |
| readonly classes | yes | yes | yes | [#1360](https://github.com/PurHur/php-compiler/issues/1360) | php-cfg Class_::flags MODIFIER_READONLY; VM rejects instance property writes after __construct |
| WeakReference / WeakMap | yes | no | yes | [#1366](https://github.com/PurHur/php-compiler/issues/1366) | VM stub: WeakReference::create/get via indirect target slot (unset clears get); not cycle-collecting GC weak refs; WeakMap uses object-id string keys; JIT may compile references but method bodies are VM-only; VM-only lowering |

_Syntax AOT column reflects `Runtime::MODE_AOT` compile probes unless a row pins AOT (e.g. native user-class link)._
## Web north-star (`examples/003-MiniWebApp`)
Expand Down
6 changes: 6 additions & 0 deletions lib/JIT/Builtin/Type/Object_.php
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,12 @@ private function registerExternalClass(string $lcname): void
$this->splObjectStorageClassId = $id;
$this->defineProperty($id, '__spl_ht', Variable::TYPE_HASHTABLE);
}
if ('weakreference' === $lcname) {
$this->defineProperty($id, '__weak_target', Variable::TYPE_NULL);
}
if ('weakmap' === $lcname) {
$this->defineProperty($id, '__weak_map', Variable::TYPE_HASHTABLE);
}
if ('phpcompiler\\vm\\variable' === $lcname) {
foreach ([
'type_undefined' => \PHPCompiler\VM\Variable::TYPE_UNDEFINED,
Expand Down
21 changes: 21 additions & 0 deletions lib/VM/Builtin/VmClassMethod.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\VM\Builtin;

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

/** VM-only class method handler; JIT call() is deferred (#1366). */
abstract class VmClassMethod extends Internal
{
public function call(Context $context, JITVariable ...$args): Value
{
throw new \LogicException(
static::class.' is not implemented for JIT in this compiler build'
);
}
}
27 changes: 27 additions & 0 deletions lib/VM/Builtin/WeakMapConstruct.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\VM\Builtin;

use PHPCompiler\Frame;
use PHPCompiler\VM\Builtin\VmClassMethod;
use PHPCompiler\VM\WeakRefSupport;

/** WeakMap::__construct() — initialize backing hashtable (#1366). */
final class WeakMapConstruct extends VmClassMethod
{
public function __construct()
{
parent::__construct('__construct');
}

public function execute(Frame $frame): void
{
if (\count($frame->calledArgs) < 1) {
throw new \LogicException('WeakMap::__construct() called without $this');
}
$receiver = WeakRefSupport::requireObject($frame->calledArgs[0], 'WeakMap');
WeakRefSupport::initMapBacking($receiver->toObject());
}
}
29 changes: 29 additions & 0 deletions lib/VM/Builtin/WeakMapCount.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\VM\Builtin;

use PHPCompiler\Frame;
use PHPCompiler\VM\Builtin\VmClassMethod;
use PHPCompiler\VM\WeakRefSupport;

final class WeakMapCount extends VmClassMethod
{
public function __construct()
{
parent::__construct('count');
}

public function execute(Frame $frame): void
{
if (\count($frame->calledArgs) < 1) {
throw new \LogicException('WeakMap::count() called without $this');
}
if (null === $frame->returnVar) {
return;
}
$receiver = WeakRefSupport::requireObject($frame->calledArgs[0], 'WeakMap');
$frame->returnVar->int(WeakRefSupport::mapTable($receiver->toObject())->getNumElements());
}
}
34 changes: 34 additions & 0 deletions lib/VM/Builtin/WeakMapOffsetExists.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\VM\Builtin;

use PHPCompiler\Frame;
use PHPCompiler\VM\Builtin\VmClassMethod;
use PHPCompiler\VM\Variable;
use PHPCompiler\VM\WeakRefSupport;

final class WeakMapOffsetExists extends VmClassMethod
{
public function __construct()
{
parent::__construct('offsetExists');
}

public function execute(Frame $frame): void
{
if (\count($frame->calledArgs) < 2) {
throw new \LogicException('WeakMap::offsetExists() expects object key');
}
if (null === $frame->returnVar) {
return;
}
$receiver = WeakRefSupport::requireObject($frame->calledArgs[0], 'WeakMap');
$key = WeakRefSupport::objectKey($frame->calledArgs[1]);
$ht = WeakRefSupport::mapTable($receiver->toObject());
$keyVar = new Variable(Variable::TYPE_STRING);
$keyVar->string($key);
$frame->returnVar->bool($ht->keyExists($keyVar));
}
}
45 changes: 45 additions & 0 deletions lib/VM/Builtin/WeakMapOffsetGet.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\VM\Builtin;

use PHPCompiler\Frame;
use PHPCompiler\VM\Builtin\VmClassMethod;
use PHPCompiler\VM\Variable;
use PHPCompiler\VM\WeakRefSupport;

final class WeakMapOffsetGet extends VmClassMethod
{
public function __construct()
{
parent::__construct('offsetGet');
}

public function execute(Frame $frame): void
{
if (\count($frame->calledArgs) < 2) {
throw new \LogicException('WeakMap::offsetGet() expects object key');
}
if (null === $frame->returnVar) {
return;
}
$receiver = WeakRefSupport::requireObject($frame->calledArgs[0], 'WeakMap');
$key = WeakRefSupport::objectKey($frame->calledArgs[1]);
$ht = WeakRefSupport::mapTable($receiver->toObject());
$keyVar = new Variable(Variable::TYPE_STRING);
$keyVar->string($key);
if (!$ht->keyExists($keyVar)) {
$frame->returnVar->null();

return;
}
$slot = $ht->findVariable($keyVar, false);
if (null === $slot) {
$frame->returnVar->null();

return;
}
$frame->returnVar->copyFrom($slot);
}
}
28 changes: 28 additions & 0 deletions lib/VM/Builtin/WeakMapOffsetSet.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\VM\Builtin;

use PHPCompiler\Frame;
use PHPCompiler\VM\Builtin\VmClassMethod;
use PHPCompiler\VM\WeakRefSupport;

final class WeakMapOffsetSet extends VmClassMethod
{
public function __construct()
{
parent::__construct('offsetSet');
}

public function execute(Frame $frame): void
{
if (\count($frame->calledArgs) < 3) {
throw new \LogicException('WeakMap::offsetSet() expects object key and value');
}
$receiver = WeakRefSupport::requireObject($frame->calledArgs[0], 'WeakMap');
$key = WeakRefSupport::objectKey($frame->calledArgs[1]);
$ht = WeakRefSupport::mapTable($receiver->toObject());
$ht->add($key, $frame->calledArgs[2]);
}
}
31 changes: 31 additions & 0 deletions lib/VM/Builtin/WeakMapOffsetUnset.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\VM\Builtin;

use PHPCompiler\Frame;
use PHPCompiler\VM\Builtin\VmClassMethod;
use PHPCompiler\VM\Variable;
use PHPCompiler\VM\WeakRefSupport;

final class WeakMapOffsetUnset extends VmClassMethod
{
public function __construct()
{
parent::__construct('offsetUnset');
}

public function execute(Frame $frame): void
{
if (\count($frame->calledArgs) < 2) {
throw new \LogicException('WeakMap::offsetUnset() expects object key');
}
$receiver = WeakRefSupport::requireObject($frame->calledArgs[0], 'WeakMap');
$key = WeakRefSupport::objectKey($frame->calledArgs[1]);
$ht = WeakRefSupport::mapTable($receiver->toObject());
$keyVar = new Variable(Variable::TYPE_STRING);
$keyVar->string($key);
$ht->offsetUnset($keyVar);
}
}
22 changes: 22 additions & 0 deletions lib/VM/Builtin/WeakReferenceConstruct.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\VM\Builtin;

use PHPCompiler\Frame;
use PHPCompiler\VM\Builtin\VmClassMethod;

/** Disallow direct instantiation (issue #1366). */
final class WeakReferenceConstruct extends VmClassMethod
{
public function __construct()
{
parent::__construct('__construct');
}

public function execute(Frame $frame): void
{
throw new \LogicException('Cannot directly instantiate WeakReference');
}
}
42 changes: 42 additions & 0 deletions lib/VM/Builtin/WeakReferenceCreate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\VM\Builtin;

use PHPCompiler\Frame;
use PHPCompiler\VM\Builtin\VmClassMethod;
use PHPCompiler\VM\ObjectEntry;
use PHPCompiler\VM\Variable;
use PHPCompiler\VM\WeakRefSupport;

/** WeakReference::create() — VM stub (#1366). */
final class WeakReferenceCreate extends VmClassMethod
{
public function __construct()
{
parent::__construct('create');
}

public function execute(Frame $frame): void
{
if (\count($frame->calledArgs) < 1) {
throw new \LogicException('WeakReference::create() expects exactly 1 argument');
}
if (null === $frame->vmContext) {
throw new \LogicException('WeakReference::create() requires VM context');
}
$object = WeakRefSupport::requireObject($frame->calledArgs[0], 'WeakReference::create() argument #1');
$class = $frame->vmContext->classes['weakreference'] ?? null;
if (null === $class) {
throw new \LogicException('WeakReference is not registered in this compiler build');
}
$entry = new ObjectEntry($class);
$ref = new Variable(Variable::TYPE_OBJECT);
$ref->object($entry);
WeakRefSupport::targetSlot($entry)->indirect($object);
if (null !== $frame->returnVar) {
$frame->returnVar->copyFrom($ref);
}
}
}
34 changes: 34 additions & 0 deletions lib/VM/Builtin/WeakReferenceGet.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\VM\Builtin;

use PHPCompiler\Frame;
use PHPCompiler\VM\Builtin\VmClassMethod;
use PHPCompiler\VM\Variable;
use PHPCompiler\VM\WeakRefSupport;

/** WeakReference::get() — VM stub (#1366). */
final class WeakReferenceGet extends VmClassMethod
{
public function __construct()
{
parent::__construct('get');
}

public function execute(Frame $frame): void
{
if (\count($frame->calledArgs) < 1) {
throw new \LogicException('WeakReference::get() called without $this');
}
$receiver = WeakRefSupport::requireObject($frame->calledArgs[0], 'WeakReference');
if (null === $frame->returnVar) {
return;
}
WeakRefSupport::copyAliveTarget(
$frame->returnVar,
WeakRefSupport::targetSlot($receiver->toObject())
);
}
}
Loading