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
28 changes: 28 additions & 0 deletions ext/dom/JitDomC14N.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ private static function tryFoldCompileTime(JITVariable $receiver): string|false|
$xml = JitDomLoadXMLUserScript::compileTimeXmlFor($receiver)
?? $receiver->compileTimeDomLoadXml
?? JitDomLoadXMLUserScript::unambiguousCompileTimeXml();
$xml = self::compileTimeXmlWithReceiverInner($receiver, $xml);
if (null === $xml
|| !JitDomLoadXMLUserScript::lastLoadWasPureUserScript()
|| JitDomLoadXMLUserScript::treeMutatedSinceLoad()
Expand Down Expand Up @@ -287,6 +288,33 @@ private static function nodeForCompileTimePath(
return $cur;
}

/**
* Rebuild loadXML literal from stamped inner after appendChild mutations (#32972 / #34862).
*
* {@see foldAnnotatedPath} reads the full document literal; LiveSlots refresh may
* update {@see JITVariable::$compileTimeDomInnerXml} without rewriting loadXml first.
*/
private static function compileTimeXmlWithReceiverInner(JITVariable $receiver, ?string $xml): ?string
{
if (null === $xml || '' === trim($xml)) {
return $xml;
}
$inner = $receiver->compileTimeDomInnerXml ?? null;
if (null === $inner || '' === $inner) {
return $xml;
}
$currentInner = DomParseSimpleXmlJitHelper::rootInnerXmlArgv($xml);
if ($inner === $currentInner) {
return $xml;
}
$parsed = DomParseSimpleXmlJitHelper::parseElementMarkupArgv($xml);
if (null === $parsed) {
return $xml;
}

return '<'.$parsed['tag'].$parsed['attrs'].'>'.$inner.'</'.$parsed['tag'].'>';
}

/** True when exclusive is non-default / non-constant — skip inclusive loadXML fold. */
private static function exclusivePreventsFold(?JITVariable $arg): bool
{
Expand Down
4 changes: 2 additions & 2 deletions ext/dom/JitDomGetElementById.php
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ private static function invokeLookup(Context $context, JITVariable ...$args): Va
$classId
);
$ht = HashTableHelper::readHashtableFromValueBox($context, $mapVar);
$foundVar = HashTableHelper::readStringKeyToValueBox($context, $ht, $idStr);
$foundVar = HashTableHelper::peekStringKeyToValueBox($context, $ht, $idStr);

return JitValueBox::valuePtrFromVariable($context, $foundVar);
}
Expand Down Expand Up @@ -235,7 +235,7 @@ private static function lookupElementIdMapBoxed(
$classId
);
$ht = HashTableHelper::readHashtableFromValueBox($context, $mapVar);
$foundVar = HashTableHelper::readStringKeyToValueBox($context, $ht, $idStr);
$foundVar = HashTableHelper::peekStringKeyToValueBox($context, $ht, $idStr);

return JitValueBox::valuePtrFromVariable($context, $foundVar);
}
Expand Down
2 changes: 1 addition & 1 deletion ext/dom/JitDomLoadXMLUserScript.php
Original file line number Diff line number Diff line change
Expand Up @@ -1248,7 +1248,7 @@ public static function storeElementInIdMapFromValueFirstWins(
$classId
);
$ht = HashTableHelper::readHashtableFromValueBox($context, $mapVar);
$foundVar = HashTableHelper::readStringKeyToValueBox($context, $ht, $idStr);
$foundVar = HashTableHelper::peekStringKeyToValueBox($context, $ht, $idStr);
$valPtr = JitValueBox::valuePtrFromVariable($context, $foundVar);
$valueMap = $context->structFieldMap['__value__'];
$i8 = $context->getTypeFromString('int8');
Expand Down
81 changes: 53 additions & 28 deletions lib/JIT/Builtin/DomNodeLiveMutationRuntime.php
Original file line number Diff line number Diff line change
Expand Up @@ -1112,40 +1112,41 @@ private static function trySyncUserScriptInnerXmlMoveToEnd(
return false;
}
// createTextNode / character-data stand-ins are never same-parent moves (#33000).
// Sticky lastFetchedChildIndex from firstChild would steal the index and rewrite
// INNER_XML to the root child markup (saveXML → <a><a>1</a></a>).
if (null !== self::compileTimeChildTextData($childArg)) {
return false;
}
$xml = JitDomLoadXMLUserScript::lastCompileTimeXml();
$markup = self::compileTimeChildElementMarkup($childArg);
if (null === $markup || '' === $markup) {
return false;
}
$xml = $receiver->compileTimeDomLoadXml
?? JitDomLoadXMLUserScript::compileTimeXmlFor($receiver)
?? JitDomLoadXMLUserScript::lastCompileTimeXml();
if (null === $xml || '' === trim($xml)) {
return false;
}
$nodes = DomParseSimpleXmlJitHelper::directChildNodesArgv($xml);
$index = $childArg->compileTimeDomChildIndex
?? JitDomNodeListItem::$lastFetchedChildIndex
?? JitDomNodeChildProperty::$lastFetchedChildIndex
?? null;
if (null === $index) {
$tag = $childArg->compileTimeDomTagName
?? JitDomNodeListItem::$lastFetchedTagName
?? JitDomNodeChildProperty::$lastFetchedTagName
?? null;
if (null === $tag || '' === $tag) {
return false;
}
foreach ($nodes as $i => $node) {
if ('element' === $node['kind'] && strtolower($tag) === strtolower($node['data'])) {
$index = $i;
break;
}
$parentInner = $receiver->compileTimeDomInnerXml
?? DomParseSimpleXmlJitHelper::rootInnerXmlArgv($xml);
if ('' === $parentInner) {
return false;
}
$chunks = DomParseSimpleXmlJitHelper::directChildMarkupChunks($parentInner);
$chunkIndex = null;
foreach ($chunks as $i => $chunk) {
if ($chunk === $markup) {
$chunkIndex = $i;
break;
}
}
if (null === $index) {
if (null === $chunkIndex) {
return false;
}
$inner = DomParseSimpleXmlJitHelper::rootInnerXmlMoveChildToEnd($xml, $index);
if (null === $inner) {
// appendChild(already-last) and repeat compile-time sync are no-ops (#34862).
if ($chunkIndex === \count($chunks) - 1) {
return true;
}
$inner = DomParseSimpleXmlJitHelper::moveChildMarkupToEnd($parentInner, $chunkIndex);
if (null === $inner || $inner === $parentInner) {
return false;
}
$receiverObj = self::receiverObject($context, $receiver);
Expand Down Expand Up @@ -1302,6 +1303,24 @@ private static function syncUserScriptInnerXmlFromArgs(
if ([] === $extraArgs) {
return;
}
$receiverObj = self::receiverObject($context, $receiver);
$xml = $receiver->compileTimeDomLoadXml
?? JitDomLoadXMLUserScript::compileTimeXmlFor($receiver)
?? JitDomLoadXMLUserScript::lastCompileTimeXml();
// Same-parent move must run before metadata concat — appendChild(firstChild)
// would duplicate markup and leave C14N fold stale (#34862 / re-#32972).
if (
$skipInnerXmlSlotMerge
&& 1 === \count($extraArgs)
&& null !== $xml
&& '' !== trim($xml)
&& JitDomLoadXMLUserScript::lastLoadWasPureUserScript()
&& self::trySyncUserScriptInnerXmlMoveToEnd($context, $receiver, $extraArgs[0])
) {
DomUserScriptLiveTagListLlvm::clearPending($context);

return;
}
$pieces = [];
$rawTexts = [];
foreach ($extraArgs as $arg) {
Expand Down Expand Up @@ -1335,11 +1354,7 @@ private static function syncUserScriptInnerXmlFromArgs(
if (!$objectType->hasProperty($classId, VmDom::PROP_USER_SCRIPT_INNER_XML)) {
$objectType->defineProperty($classId, VmDom::PROP_USER_SCRIPT_INNER_XML, Variable::TYPE_STRING);
}
$receiverObj = self::receiverObject($context, $receiver);
// Refresh C14N fold from the *receiver* document's loadXML (#32972 / #32987 / #33000).
$xml = $receiver->compileTimeDomLoadXml
?? JitDomLoadXMLUserScript::compileTimeXmlFor($receiver)
?? JitDomLoadXMLUserScript::lastCompileTimeXml();
$childIndex = $receiver->compileTimeDomChildIndex
?? JitDomNodeChildProperty::$lastFetchedChildIndex
?? null;
Expand Down Expand Up @@ -1422,6 +1437,16 @@ private static function syncUserScriptInnerXmlFromArgs(
// importNode + appendChild used the source literal as SSOT and doubled inner markup
// (#34302 / #34405 leftover).
DomUserScriptLiveTagListLlvm::clearPending($context);
// Refresh C14N fold from stamped inner after createElement append (#32972).
$inner = $receiver->compileTimeDomInnerXml ?? null;
if (
null !== $inner
&& '' !== $inner
&& null !== $xml
&& JitDomLoadXMLUserScript::lastLoadWasPureUserScript()
) {
JitDomLoadXMLUserScript::refreshCompileTimeXmlWithRootInner($inner, $receiver);
}

return;
}
Expand Down
6 changes: 6 additions & 0 deletions lib/JIT/HashTableHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,12 @@ public static function readSuperglobalStringKeyToValueBox(
return HashTableReadLlvm::readSuperglobalStringKeyToValueBox($context, $ht, $keyStr);
}

/** Missing-key read without Zend undefined-index warning (#35648 / getElementById id map). */
public static function peekStringKeyToValueBox(Context $context, Value $ht, Value $keyStr): Variable
{
return HashTableReadLlvm::readSuperglobalStringKeyToValueBox($context, $ht, $keyStr);
}

public static function readStringKeyToValueBox(Context $context, Value $ht, Value $keyStr): Variable
{
return HashTableReadLlvm::readStringKeyToValueBox($context, $ht, $keyStr);
Expand Down
Loading