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
19 changes: 5 additions & 14 deletions ext/dom/DomSetIdAttributeJitHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace PHPCompiler\ext\dom;

use PHPCompiler\VM\Context;
use PHPCompiler\VM\ObjectEntry;

/**
Expand Down Expand Up @@ -47,23 +48,13 @@ public static function setIdNsFalseArgv(ObjectEntry $element, string $namespace,
VmDom::setIdAttributeNS($element, $ns, $localName, false);
}

public static function setIdNodeTrueArgv(ObjectEntry $element, ObjectEntry $attr): void
public static function setIdNodeTrueArgv(Context $ctx, ObjectEntry $attr): void
{
VmDom::syncDomRegistryParentChainFromProperties($element);
$qName = VmDom::resolveThinAotSetIdNodeQName($element, $attr);
if ('' !== $qName) {
VmDom::seedThinAotElementAttribute($element, $qName, VmDom::thinAttrValue($attr));
}
VmDom::setIdAttributeNode($element, $attr, true);
VmDom::setIdAttributeNodeOnAttrOwner($attr, true);
}

public static function setIdNodeFalseArgv(ObjectEntry $element, ObjectEntry $attr): void
public static function setIdNodeFalseArgv(Context $ctx, ObjectEntry $attr): void
{
VmDom::syncDomRegistryParentChainFromProperties($element);
$qName = VmDom::resolveThinAotSetIdNodeQName($element, $attr);
if ('' !== $qName) {
VmDom::seedThinAotElementAttribute($element, $qName, VmDom::thinAttrValue($attr));
}
VmDom::setIdAttributeNode($element, $attr, false);
VmDom::setIdAttributeNodeOnAttrOwner($attr, false);
}
}
8 changes: 4 additions & 4 deletions ext/dom/JitDomDocumentMethodKernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -1153,11 +1153,11 @@ public static function ensureSetIdAttributeNsFalseBridge(Context $context): void
public static function ensureSetIdAttributeNodeTrueBridge(Context $context): void
{
$objPtr = $context->getTypeFromString('__object__*');
self::ensureBridge(
self::ensureContextBridge(
$context,
\PHPCompiler\JIT\Builtin\DomSetIdAttributeRuntime::ABI_NODE_TRUE,
'dom_element_set_id_attribute_node_true_user_script',
[$objPtr, $objPtr],
[$objPtr],
$context->context->voidType(),
'PHPCompiler\\ext\\dom\\DomSetIdAttributeJitHelper::setIdNodeTrueArgv',
'/ext/dom/DomSetIdAttributeJitHelper.php'
Expand All @@ -1167,11 +1167,11 @@ public static function ensureSetIdAttributeNodeTrueBridge(Context $context): voi
public static function ensureSetIdAttributeNodeFalseBridge(Context $context): void
{
$objPtr = $context->getTypeFromString('__object__*');
self::ensureBridge(
self::ensureContextBridge(
$context,
\PHPCompiler\JIT\Builtin\DomSetIdAttributeRuntime::ABI_NODE_FALSE,
'dom_element_set_id_attribute_node_false_user_script',
[$objPtr, $objPtr],
[$objPtr],
$context->context->voidType(),
'PHPCompiler\\ext\\dom\\DomSetIdAttributeJitHelper::setIdNodeFalseArgv',
'/ext/dom/DomSetIdAttributeJitHelper.php'
Expand Down
4 changes: 2 additions & 2 deletions ext/dom/JitDomSetIdAttribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,6 @@ public static function invokeNode(Context $context, JITVariable ...$args): Value
return self::invoke($context, $args[0], $nameVar, $args[2]);
}

$element = self::loadObjectArg($context, $args[0]);
$attr = $context->builder->call(
$context->lookupFunction('__value__readObject'),
JitValueBox::valuePtrFromVariable($context, $args[1])
Expand All @@ -247,17 +246,18 @@ public static function invokeNode(Context $context, JITVariable ...$args): Value
}
$context->builder->call(
$context->lookupFunction($abi),
$element,
$attr
);
if (JitDomDocumentMethodKernel::shouldUse($context) && $isIdTrue) {
BasicBlockHelper::ensureOpenInsertBlock($context, 'dom_set_id_attribute_node_post');
$element = self::loadObjectArg($context, $args[0]);
self::storeCacheFromRuntimeAttrValue($context, $element, $attr);
$key = JitDomAttrRename::lastFetchedKey();
if (null !== $key) {
DomUserScriptAttributeCacheLlvm::markIdBearingLiteral($key[0], $key[1], true);
}
} elseif (JitDomDocumentMethodKernel::shouldUse($context) && !$isIdTrue) {
$element = self::loadObjectArg($context, $args[0]);
DomUserScriptElementCacheLlvm::invalidateIfElement($context, $element);
}

Expand Down
24 changes: 24 additions & 0 deletions ext/dom/VmDom.php
Original file line number Diff line number Diff line change
Expand Up @@ -2967,6 +2967,30 @@ private static function setIdAttributeNSInternal(
self::applyIdAttributeRegistration($element, $qName, $isId, $syncIdMap);
}

/**
* Thin-AOT helper entry: resolve owner from Attr and delegate (#33758).
*/
public static function setIdAttributeNodeOnAttrOwner(ObjectEntry $attr, bool $isId): void
{
if (!self::isAttr($attr)) {
throw new \TypeError('DOMElement::setIdAttributeNode(): Argument #1 ($attr) must be of type DOMAttr');
}
if (!$attr->hasProperty(self::PROP_OWNER_ELEMENT)) {
return;
}
$ownerVar = $attr->getProperty(self::PROP_OWNER_ELEMENT)->resolveIndirect();
if (Variable::TYPE_OBJECT !== $ownerVar->type) {
return;
}
$element = $ownerVar->toObject();
self::syncDomRegistryParentChainFromProperties($element);
$qName = self::resolveThinAotSetIdNodeQName($element, $attr);
if ('' !== $qName) {
self::seedThinAotElementAttribute($element, $qName, self::thinAttrValue($attr));
}
self::setIdAttributeNode($element, $attr, $isId);
}

/**
* DOMElement::setIdAttributeNode() — mark an owned Attr as ID (php-src ext/dom/element.c; #20123).
* Zend: NOT_FOUND_ERR when attr->parent != element.
Expand Down
4 changes: 2 additions & 2 deletions lib/JIT/Builtin/DomSetIdAttributeRuntime.php
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,11 @@ private static function linkNs(Context $context, string $abi, string $entry, str
private static function linkNode(Context $context, string $abi, string $entry, string $helper): void
{
$objPtr = $context->getTypeFromString('__object__*');
JitVmHelperLink::ensureBridge(
JitVmHelperLink::ensureContextBridge(
$context,
$abi,
$entry,
[$objPtr, $objPtr],
[$objPtr],
$context->context->voidType(),
$helper,
self::HELPER_PATH,
Expand Down
9 changes: 6 additions & 3 deletions lib/JIT/Call/DomAttrIsId.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,18 @@ public function call(Context $context, Variable ...$args): Value
}
$key = JitDomAttrRename::lastFetchedKey();
if (null !== $key) {
// loadXML DTD ATTLIST ID / xml:id / setIdAttribute* stamp compile-time flags (#34821).
// Must precede the user-function {@code id} global — activeFunction is the script
// name in {main}, not only nested closures (#23514).
if (DomUserScriptAttributeCacheLlvm::isIdBearingLiteral($key[0], $key[1])) {
return $context->getTypeFromString('int1')->constInt(1, false);
}
$active = $context->activeFunction;
if ('' !== $active && !str_starts_with($active, '__')) {
// createElement id= toggles via module global — runtime stores from setIdAttribute (#29884).
if ('' === $key[0] && 'id' === $key[1]) {
return DomUserScriptAttributeCacheLlvm::loadIdBearingGlobal($context);
}
if (DomUserScriptAttributeCacheLlvm::isIdBearingLiteral($key[0], $key[1])) {
return $context->getTypeFromString('int1')->constInt(1, false);
}
}

return DomAttrIsIdRuntime::invoke($context, $args[0]);
Expand Down
91 changes: 91 additions & 0 deletions lib/JIT/JitVmHelperLink.php
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,97 @@ public static function ensureBridge(
}
}

/**
* ABI bridge with implicit VmActiveContext as helper arg 0 (#33758 / peer DomAttrIsIdJitHelper).
*
* @param list<Type> $paramTypes ABI parameter types (helper receives Context + these)
*/
public static function ensureContextBridge(
Context $context,
string $abiName,
string $entryBlockName,
array $paramTypes,
Type $returnType,
string $helperLogical,
string $relativeHelperPath,
array $compiledHelpers,
string $issueTag,
bool $skipHelperRuntimeCache = false
): void {
$probe = $context->module->getNamedFunction($abiName);
if (self::hasNamedBridgeEntry($probe, $entryBlockName)) {
$context->registerFunction($abiName, $probe);

return;
}

$savedBlock = null;
try {
$savedBlock = $context->builder->getInsertBlock();
} catch (\Throwable) {
}

self::ensureCompiled(
$context,
$relativeHelperPath,
$compiledHelpers,
$issueTag,
$skipHelperRuntimeCache
);

$helperFn = self::lookupCompiled($context, $helperLogical, $issueTag);
$ft = $context->context->functionType($returnType, false, ...$paramTypes);
$fn = null !== $probe
? $probe
: $context->module->addFunction($abiName, $ft);

$entry = self::bridgeEntryForEmit($fn, $entryBlockName);
$context->builder->positionAtEnd($entry);
$vmCtx = $context->builder->call(VmActiveContextLlvm::lookupAbi($context));
$args = [
JitNestedHelperCoerce::coerceArgForHelper(
$context,
$vmCtx,
$helperFn->getParam(0)->typeOf()
),
];
for ($i = 0, $n = $fn->countParams(); $i < $n; ++$i) {
$args[] = JitNestedHelperCoerce::coerceArgForHelper(
$context,
$fn->getParam($i),
$helperFn->getParam($i + 1)->typeOf()
);
}
$result = $context->builder->call($helperFn, ...$args);
if ('void' === $context->getStringFromType($returnType)) {
$context->builder->returnVoid();
} else {
$ret = JitNestedHelperCoerce::coerceBridgeResult($context, $result, $returnType);
$context->builder->returnValue($ret);
}
$context->registerFunction($abiName, $fn);

if (null !== $savedBlock) {
$context->builder->positionAtEnd($savedBlock);
} else {
$fallback = null;
if ('' !== $context->activeFunction && isset($context->functions[$context->activeFunction])) {
$active = $context->functions[$context->activeFunction];
if ($active instanceof LlvmFunction) {
$fallback = $active;
}
}
if (null === $fallback && $context->main instanceof LlvmFunction) {
$fallback = $context->main;
}
if (null !== $fallback && $fallback->countBasicBlocks() > 0) {
$context->builder->positionAtEnd($fallback->getEntryBasicBlock());
} else {
$context->builder->clearInsertionPosition();
}
}
}

/**
* Resolve helper source path: ext/* and lib/* live at repo root; /VM/* under lib/.
*/
Expand Down
Loading