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
392 changes: 214 additions & 178 deletions docs/bootstrap-inventory.md

Large diffs are not rendered by default.

14 changes: 11 additions & 3 deletions ext/tokenizer/VmPhpToken.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,8 @@ public static function tokenize(Context $ctx, string $code, int $flags = 0): arr
throw new \LogicException('PhpToken is not registered in this compiler build');
}

$raw = LanguageScanner::tokenize($code, $flags);
$parts = self::normalizeTokensWithPositions($code, $raw);
$out = [];
foreach ($parts as $part) {
foreach (self::tokenizeParts($code, $flags) as $part) {
$out[] = self::createObject(
$class,
$part['id'],
Expand All @@ -92,6 +90,16 @@ public static function tokenize(Context $ctx, string $code, int $flags = 0): arr
return $out;
}

/**
* Token stream without ClassEntry — JIT/AOT materialization (#27263).
*
* @return list<array{id: int, text: string, line: int, pos: int}>
*/
public static function tokenizeParts(string $code, int $flags = 0): array
{
return self::normalizeTokensWithPositions($code, LanguageScanner::tokenize($code, $flags));
}

public static function createObject(
ClassEntry $class,
int $id,
Expand Down
19 changes: 19 additions & 0 deletions lib/JIT/Builtin/PhpTokenRuntime.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\JIT\Builtin;

use PHPCompiler\JIT\Context;

/**
* PhpToken JIT/AOT markers (#27263) — name lookup is inlined in
* {@see \PHPCompiler\JIT\Call\PhpTokenGetTokenName} (no NestedJIT).
*/
final class PhpTokenRuntime
{
public static function ensureLinked(Context $context): void
{
$context->type->object->lookup('PhpToken');
}
}
16 changes: 16 additions & 0 deletions lib/JIT/Builtin/Type/Object_.php
Original file line number Diff line number Diff line change
Expand Up @@ -3363,6 +3363,22 @@ private function registerExternalClass(string $lcname, string $displayName): voi
// Thin user-script AOT must call __construct (not allocate-only) (#27303 / #26772).
$this->markHasConstructor($id);
}
if ('phptoken' === $lcname) {
// php-src PhpToken public $id/$text/$line/$pos (#27263 / #6794).
$this->defineProperty($id, \PHPCompiler\ext\tokenizer\VmPhpToken::PROP_ID, Variable::TYPE_NATIVE_LONG);
$this->defineProperty($id, \PHPCompiler\ext\tokenizer\VmPhpToken::PROP_TEXT, Variable::TYPE_VALUE);
$this->defineProperty($id, \PHPCompiler\ext\tokenizer\VmPhpToken::PROP_LINE, Variable::TYPE_NATIVE_LONG);
$this->defineProperty($id, \PHPCompiler\ext\tokenizer\VmPhpToken::PROP_POS, Variable::TYPE_NATIVE_LONG);
$this->markHasConstructor($id);
$pub = \PHPCfg\Func::FLAG_PUBLIC;
$pubStatic = $pub | \PHPCfg\Func::FLAG_STATIC;
$this->defineMethodVisibility($id, '__construct', $pub);
$this->defineMethodVisibility($id, 'tokenize', $pubStatic);
$this->defineMethodVisibility($id, 'gettokenname', $pub, 'getTokenName');
$this->defineMethodVisibility($id, 'is', $pub);
$this->defineMethodVisibility($id, 'isignorable', $pub, 'isIgnorable');
$this->defineMethodVisibility($id, '__tostring', $pub, '__toString');
}
if ('reflectionenum' === $lcname) {
$this->defineProperty($id, 'name', Variable::TYPE_STRING);
}
Expand Down
95 changes: 95 additions & 0 deletions lib/JIT/Call/PhpTokenConstruct.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\JIT\Call;

use PHPCompiler\ext\tokenizer\VmPhpToken;
use PHPCompiler\JIT\Builtin\ReflectionSetup;
use PHPCompiler\JIT\Call;
use PHPCompiler\JIT\Context;
use PHPCompiler\JIT\JitLongArg;
use PHPCompiler\JIT\JitValueBox;
use PHPCompiler\JIT\Variable;
use PHPLLVM\Value;

/**
* PhpToken::__construct(int $id, string $text, int $line = -1, int $pos = -1) — JIT/AOT (#27263).
*
* php-src: ext/tokenizer/tokenizer.c — PhpToken::__construct
* VM SSOT: {@see \PHPCompiler\ext\tokenizer\PhpTokenConstruct}
*/
final class PhpTokenConstruct implements Call
{
public function call(Context $context, Variable ...$args): Value
{
if (\count($args) < 3) {
throw new \ArgumentCountError(
'PhpToken::__construct() expects at least 2 arguments, '.\max(0, \count($args) - 1).' given'
);
}
$obj = ReflectionSetup::loadObjectFromArg($context, $args[0]);
$context->type->object->lookup('PhpToken');

$id = JitLongArg::lower($context, $args[1], 'PhpToken::__construct id');
$idVar = new Variable(
$context,
Variable::TYPE_NATIVE_LONG,
Variable::KIND_VALUE,
$id
);
$context->type->object->propertyStore(
$context->type->object->propertySlotFor($obj, 'PhpToken', VmPhpToken::PROP_ID),
$idVar,
Variable::TYPE_NATIVE_LONG
);

ReflectionSetup::emitSetStringPropertyFromVar(
$context,
$obj,
'PhpToken',
VmPhpToken::PROP_TEXT,
$args[2]
);

$line = \count($args) >= 4
? JitLongArg::lower($context, $args[3], 'PhpToken::__construct line')
: $context->constantFromInteger(-1);
$lineVar = new Variable(
$context,
Variable::TYPE_NATIVE_LONG,
Variable::KIND_VALUE,
$line
);
$context->type->object->propertyStore(
$context->type->object->propertySlotFor($obj, 'PhpToken', VmPhpToken::PROP_LINE),
$lineVar,
Variable::TYPE_NATIVE_LONG
);

$pos = \count($args) >= 5
? JitLongArg::lower($context, $args[4], 'PhpToken::__construct pos')
: $context->constantFromInteger(-1);
$posVar = new Variable(
$context,
Variable::TYPE_NATIVE_LONG,
Variable::KIND_VALUE,
$pos
);
$context->type->object->propertyStore(
$context->type->object->propertySlotFor($obj, 'PhpToken', VmPhpToken::PROP_POS),
$posVar,
Variable::TYPE_NATIVE_LONG
);

ReflectionSetup::markConstructed($context, $obj);

$slot = JitValueBox::alloc($context);
$context->builder->call(
$context->lookupFunction('__value__writeNull'),
JitValueBox::pointer($context, $slot)
);

return $slot;
}
}
116 changes: 116 additions & 0 deletions lib/JIT/Call/PhpTokenGetTokenName.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\JIT\Call;

use PHPCompiler\ext\tokenizer\TokenConstants;
use PHPCompiler\ext\tokenizer\VmPhpToken;
use PHPCompiler\JIT\BasicBlockHelper;
use PHPCompiler\JIT\Builtin\PhpTokenRuntime;
use PHPCompiler\JIT\Builtin\ReflectionSetup;
use PHPCompiler\JIT\Call;
use PHPCompiler\JIT\Context;
use PHPCompiler\JIT\JitValueBox;
use PHPCompiler\JIT\Variable;
use PHPLLVM\Builder;
use PHPLLVM\Value;

/**
* PhpToken::getTokenName(): ?string — JIT/AOT (#27263).
*
* Inline id→name select-walk (peer GetClassRuntime #26854).
*
* php-src: ext/tokenizer/tokenizer.c — PhpToken::getTokenName
* VM SSOT: {@see \PHPCompiler\ext\tokenizer\PhpTokenGetTokenName}
*/
final class PhpTokenGetTokenName implements Call
{
public function call(Context $context, Variable ...$args): Value
{
if ([] === $args) {
throw new \LogicException('PhpToken::getTokenName() requires $this');
}
PhpTokenRuntime::ensureLinked($context);
$obj = ReflectionSetup::loadObjectFromArg($context, $args[0]);
$id = ReflectionSetup::integerPropertyAsI64(
$context,
$obj,
'PhpToken',
VmPhpToken::PROP_ID
);

$named = self::emitNameSelectWalk($context, $id);
$i8p = $context->getTypeFromString('int8*');
$i64 = $context->getTypeFromString('int64');
$raw = $context->builder->pointerCast($named, $i8p);
$lenPtr = $context->builder->pointerCast(
$context->builder->gep($raw, $context->constantFromInteger(8, 'size_t')),
$context->getTypeFromString('int64*')
);
$nameLen = $context->builder->load($lenPtr);

$namedBb = BasicBlockHelper::append($context, 'phptoken_gtn_named');
$fallbackBb = BasicBlockHelper::append($context, 'phptoken_gtn_fallback');
$doneBb = BasicBlockHelper::append($context, 'phptoken_gtn_done');
$hasName = $context->builder->icmp(Builder::INT_NE, $nameLen, $i64->constInt(0, false));
$context->builder->branchIf($hasName, $namedBb, $fallbackBb);

$retSlot = JitValueBox::alloc($context);
$retPtr = JitValueBox::pointer($context, $retSlot);

$context->builder->positionAtEnd($namedBb);
$context->builder->call($context->lookupFunction('__value__writeString'), $retPtr, $named);
$context->builder->branch($doneBb);

$context->builder->positionAtEnd($fallbackBb);
[$textCstr, $textLen] = ReflectionSetup::stringPropertyAsCstr(
$context,
$obj,
'PhpToken',
VmPhpToken::PROP_TEXT
);
$isSingle = $context->builder->icmp(
Builder::INT_EQ,
$context->builder->zExt($textLen, $i64),
$i64->constInt(1, false)
);
$fbSingleBb = BasicBlockHelper::append($context, 'phptoken_gtn_single');
$fbNullBb = BasicBlockHelper::append($context, 'phptoken_gtn_null');
$context->builder->branchIf($isSingle, $fbSingleBb, $fbNullBb);

$context->builder->positionAtEnd($fbSingleBb);
$textStr = $context->builder->call(
$context->lookupFunction('__string__init'),
$context->builder->zExt($textLen, $i64),
$textCstr
);
$context->builder->call($context->lookupFunction('__value__writeString'), $retPtr, $textStr);
$context->builder->branch($doneBb);

$context->builder->positionAtEnd($fbNullBb);
$context->builder->call($context->lookupFunction('__value__writeNull'), $retPtr);
$context->builder->branch($doneBb);

$context->builder->positionAtEnd($doneBb);

return $retSlot;
}

/** @return Value {@see __string__*} */
private static function emitNameSelectWalk(Context $context, Value $tokenId): Value
{
$result = $context->builder->load($context->constantStringFromString(''));
foreach (TokenConstants::registeredConstants() as $name => $id) {
if ('TOKEN_PARSE' === $name) {
continue;
}
$expected = $context->constantFromInteger((int) $id, 'int64');
$isId = $context->builder->icmp(Builder::INT_EQ, $tokenId, $expected);
$candidate = $context->builder->load($context->constantStringFromString((string) $name));
$result = $context->builder->select($isId, $candidate, $result);
}

return $result;
}
}
Loading
Loading