Skip to content

Commit 75978d9

Browse files
committed
Remove unnecessary RuntimeContext data/frame split
1 parent 578aa58 commit 75978d9

4 files changed

Lines changed: 23 additions & 24 deletions

File tree

src/Compiler.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,7 @@ private static function templateClosure(string $code, string $partials = '', str
9494
$use = $useVars !== '' ? " use ($useVars)" : '';
9595
return <<<PHP
9696
function (mixed \$in = null, array \$options = [])$use {
97-
\$cx = LR::createContext(\$in, \$options, [$partials]);
98-
\$cx->frame['root'] = &\$cx->data['root'];$stmts
97+
\$cx = LR::createContext(\$in, \$options, [$partials]);$stmts
9998
return $code;
10099
}
101100
PHP;
@@ -819,7 +818,7 @@ private function getSimpleHelperName(PathExpression|Literal $path): ?string
819818
private function buildBasePath(bool $data, int $depth): string
820819
{
821820
if ($data) {
822-
return '$cx->frame' . str_repeat("['_parent']", $depth);
821+
return '$cx->data' . str_repeat("['_parent']", $depth);
823822
}
824823
return $depth > 0 ? "\$cx->depths[count(\$cx->depths)-$depth]" : '$in';
825824
}

src/HelperOptions.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,12 @@ private function invokeBlock(\Closure $closure, mixed $context, mixed $data): st
9797
// HBS.js options.fn(this) / options.inverse(this)), since the scope level isn't changing.
9898
$pushDepths = $context !== $scope;
9999
$resolvedContext = $pushDepths ? ($context === Scope::Use ? $scope : $context) : $scope;
100-
$savedFrame = null;
100+
$outerFrame = null;
101101
$bpStack = null;
102102

103103
if (isset($data['data'])) {
104-
$savedFrame = $cx->frame;
105-
$cx->frame = $data['data'];
104+
$outerFrame = $cx->data;
105+
$cx->data = $data['data'];
106106
}
107107

108108
if (isset($data['blockParams'])) {
@@ -119,8 +119,8 @@ private function invokeBlock(\Closure $closure, mixed $context, mixed $data): st
119119
if ($pushDepths) {
120120
array_pop($cx->depths);
121121
}
122-
if ($savedFrame !== null) {
123-
$cx->frame = $savedFrame;
122+
if ($outerFrame !== null) {
123+
$cx->data = $outerFrame;
124124
}
125125
$cx->inlinePartials = $savedInlinePartials;
126126
return $ret;
@@ -154,7 +154,7 @@ public function iterate(array $items): string
154154
$last = count($items) - 1;
155155
$ret = '';
156156
$i = 0;
157-
$outerFrame = $cx->frame;
157+
$outerFrame = $cx->data;
158158
// Pre-allocate bpStack once; mutate [0][0] and [0][1] per iteration.
159159
// PHP COW ensures the inner array's refcount returns to 1 after $cb() returns,
160160
// so the next iteration's assignment is an in-place mutation, not a copy.
@@ -166,7 +166,7 @@ public function iterate(array $items): string
166166
$data['key'] = $index;
167167
$data['index'] = $i;
168168
$data['last'] = $i === $last;
169-
$cx->frame = $data;
169+
$cx->data = $data;
170170

171171
$bpStack[0][0] = $value;
172172
$bpStack[0][1] = $index;
@@ -175,7 +175,7 @@ public function iterate(array $items): string
175175
$i++;
176176
}
177177

178-
$cx->frame = $outerFrame;
178+
$cx->data = $outerFrame;
179179
array_pop($cx->depths);
180180
$cx->inlinePartials = $savedInlinePartials;
181181
return $ret;

src/Runtime.php

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -169,31 +169,33 @@ public static function lookupLength(mixed $base, bool $strict = false): mixed
169169
public static function createContext(mixed $context, array $options, array $compiledPartials): RuntimeContext
170170
{
171171
$parentCx = self::$partialContext;
172-
$root = ['root' => $context];
173172

174173
if ($parentCx !== null) {
175174
// Partial context: reuse the parent's already-merged helpers and partials directly.
176-
// PHP copy-on-write ensures inlinePartials is only copied if in() registers a new inline partial.
177-
// Inherit the parent's current frame so @index, @key, etc. remain accessible inside partials.
178-
// templateClosure will update frame['root'] to reference this partial's own data['root'].
175+
// PHP copy-on-write ensures inlinePartials is only copied if fn() registers a new inline partial.
176+
// Inherit the parent's current data so @index, @key, etc. remain accessible inside partials.
177+
// Unset 'root' first to break the reference established by `$in = &$cx->data['root']` in the
178+
// calling template; a direct assignment would write through it and corrupt the caller's $in.
179+
$data = $parentCx->data;
180+
unset($data['root']);
181+
$data['root'] = $context;
179182
return new RuntimeContext(
180183
helpers: $parentCx->helpers,
181184
partials: $parentCx->partials,
182185
inlinePartials: $parentCx->inlinePartials,
183186
depths: $parentCx->depths,
184-
data: $root,
185-
frame: $parentCx->frame,
187+
data: $data,
186188
partialBlock: $parentCx->partialBlock,
187189
);
188190
}
189191

190192
$data = $options['data'] ?? [];
193+
$data['root'] = $data['root'] ?? $context;
191194
$extraHelpers = $options['helpers'] ?? [];
192195
return new RuntimeContext(
193196
helpers: $extraHelpers ? array_replace(Runtime::defaultHelpers(), $extraHelpers) : Runtime::defaultHelpers(),
194197
partials: array_replace($compiledPartials, $options['partials'] ?? []),
195-
data: ['root' => $data['root'] ?? $context],
196-
frame: $data,
198+
data: $data,
197199
);
198200
}
199201

@@ -316,7 +318,7 @@ public static function sec(RuntimeContext $cx, mixed $value, mixed $in, ?\Closur
316318
// with no arguments, mirroring HBS.js which does not treat them as helper calls.
317319
if ($value instanceof \Closure) {
318320
$result = $helperName !== null
319-
? $value(new HelperOptions(scope: $in, data: $cx->frame, cx: $cx, cb: $cb, inv: $else))
321+
? $value(new HelperOptions(scope: $in, data: $cx->data, cx: $cx, cb: $cb, inv: $else))
320322
: $value();
321323
return static::resolveBlockResult($cx, $result, $in, $cb, $else);
322324
}
@@ -473,7 +475,7 @@ public static function hbch(RuntimeContext $cx, \Closure $helper, string $name,
473475
if ($numParams === 0 || $numParams > count($positional)) {
474476
$positional[] = new HelperOptions(
475477
scope: $_this,
476-
data: $cx->frame,
478+
data: $cx->data,
477479
cx: $cx,
478480
name: $name,
479481
hash: $hash,
@@ -497,7 +499,7 @@ public static function hbbch(RuntimeContext $cx, \Closure $helper, string $name,
497499
{
498500
$positional[] = new HelperOptions(
499501
scope: $_this,
500-
data: $cx->frame,
502+
data: $cx->data,
501503
cx: $cx,
502504
name: $name,
503505
hash: $hash,

src/RuntimeContext.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,13 @@ final class RuntimeContext
1313
* @param array<string, \Closure> $inlinePartials block-scoped {{#* inline}} partials (reset on fn() return)
1414
* @param array<mixed> $depths
1515
* @param array<mixed> $data
16-
* @param array<mixed> $frame
1716
*/
1817
public function __construct(
1918
public array $helpers = [],
2019
public array $partials = [],
2120
public array $inlinePartials = [],
2221
public array $depths = [],
2322
public array $data = [],
24-
public array $frame = [],
2523
public ?\Closure $partialBlock = null,
2624
) {}
2725
}

0 commit comments

Comments
 (0)