packages/deriver/tests/Unit/Deriver/Evaluator/Execution/Transfer/MemoryStepTest.php

1<?php
2
3declare (strict_types=1);
4
5namespace Tests\Unit\Deriver\Evaluator\Execution\Transfer;
6
7use Deriver\Deriver;
8use Deriver\Deriver\Candidate\ResultRef;
9use Deriver\Deriver\Candidate\Snapshot;
10use Deriver\Deriver\Candidate\Statistics;
11use Deriver\Deriver\Contributions;
12use Deriver\Deriver\DeriverConfig;
13use Deriver\Deriver\Evaluator\Call\BoundInstruction;
14use Deriver\Deriver\Evaluator\Call\Evaluators;
15use Deriver\Deriver\Evaluator\Call\Extensions;
16use Deriver\Deriver\Evaluator\Call\Registrations;
17use Deriver\Deriver\Evaluator\Call\StateRegistry;
18use Deriver\Deriver\Evaluator\EvaluatorConfig;
19use Deriver\Deriver\Evaluator\Evidence\Frontier;
20use Deriver\Deriver\Evaluator\Execution\Call\ArgumentBinding;
21use Deriver\Deriver\Evaluator\Execution\Call\ArgumentOrder;
22use Deriver\Deriver\Evaluator\Execution\Call\CallableCheck;
23use Deriver\Deriver\Evaluator\Execution\Call\CallExecutor;
24use Deriver\Deriver\Evaluator\Execution\Call\CallResolution;
25use Deriver\Deriver\Evaluator\Execution\Call\Native\Properties;
26use Deriver\Deriver\Evaluator\Execution\Call\Native\Signatures;
27use Deriver\Deriver\Evaluator\Execution\Call\ParameterBinding;
28use Deriver\Deriver\Evaluator\Execution\Call\PassedArgument;
29use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Arguments;
30use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Modes;
31use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Resolution;
32use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Target;
33use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Transfer;
34use Deriver\Deriver\Evaluator\Execution\Call\TypeBinding;
35use Deriver\Deriver\Evaluator\Execution\Call\TypeCheck;
36use Deriver\Deriver\Evaluator\Execution\Completion\Alternative;
37use Deriver\Deriver\Evaluator\Execution\Completion\Assessment;
38use Deriver\Deriver\Evaluator\Execution\Completion\Completion;
39use Deriver\Deriver\Evaluator\Execution\Completion\StorageSnapshot;
40use Deriver\Deriver\Evaluator\Execution\Constraints;
41use Deriver\Deriver\Evaluator\Execution\Context;
42use Deriver\Deriver\Evaluator\Execution\Control\ExceptionChain;
43use Deriver\Deriver\Evaluator\Execution\Control\ExceptionMatch;
44use Deriver\Deriver\Evaluator\Execution\Control\Handler;
45use Deriver\Deriver\Evaluator\Execution\Control\ObservationLimit;
46use Deriver\Deriver\Evaluator\Execution\Control\StateJoin;
47use Deriver\Deriver\Evaluator\Execution\Control\Unwinding;
48use Deriver\Deriver\Evaluator\Execution\Demand\Discovery;
49use Deriver\Deriver\Evaluator\Execution\Dependencies;
50use Deriver\Deriver\Evaluator\Execution\Evaluator;
51use Deriver\Deriver\Evaluator\Execution\EvaluatorConfig as ExecutionEvaluatorConfig;
52use Deriver\Deriver\Evaluator\Execution\InstructionTransfer;
53use Deriver\Deriver\Evaluator\Execution\Memory\Location;
54use Deriver\Deriver\Evaluator\Execution\Memory\Materialization;
55use Deriver\Deriver\Evaluator\Execution\Memory\Memory;
56use Deriver\Deriver\Evaluator\Execution\Memory\ReferenceConstraint;
57use Deriver\Deriver\Evaluator\Execution\Memory\Slots\SlotReference;
58use Deriver\Deriver\Evaluator\Execution\Memory\StorageCapture;
59use Deriver\Deriver\Evaluator\Execution\ObservationCollector;
60use Deriver\Deriver\Evaluator\Execution\Offset\Address;
61use Deriver\Deriver\Evaluator\Execution\Offset\Path;
62use Deriver\Deriver\Evaluator\Execution\Offset\Transfer as OffsetTransfer;
63use Deriver\Deriver\Evaluator\Execution\Operation\Conversions;
64use Deriver\Deriver\Evaluator\Execution\Operation\ScalarErrors;
65use Deriver\Deriver\Evaluator\Execution\State;
66use Deriver\Deriver\Evaluator\Execution\Summary\Evaluation;
67use Deriver\Deriver\Evaluator\Execution\Summary\Isolation;
68use Deriver\Deriver\Evaluator\Execution\Trace\Derivation;
69use Deriver\Deriver\Evaluator\Execution\Transfer\MemoryStep;
70use Deriver\Deriver\Evaluator\Execution\Transfer\PropertySlot;
71use Deriver\Deriver\Evaluator\Execution\Transfer\PureStep;
72use Deriver\Deriver\Evaluator\Execution\Transfer\ReferenceAssignment;
73use Deriver\Deriver\Evaluator\Request\Budget;
74use Deriver\Deriver\Evaluator\Request\EntryPoint;
75use Deriver\Deriver\Evaluator\Request\QueryScope;
76use Deriver\Deriver\Evaluator\Request\ReturnQuery;
77use Deriver\Deriver\Evaluator\Scalar\Arithmetic;
78use Deriver\Deriver\Evaluator\Scalar\Arrays;
79use Deriver\Deriver\Evaluator\Scalar\Identity;
80use Deriver\Deriver\Evaluator\Scalar\Increment;
81use Deriver\Deriver\Evaluator\Scalar\Operations;
82use Deriver\Deriver\Evaluator\TargetProfile;
83use Deriver\Deriver\Evaluator\Term;
84use Deriver\Deriver\Evaluator\TermKind;
85use Deriver\Deriver\Execution\DerivationResult;
86use Deriver\Deriver\Execution\QueryExecution;
87use Deriver\Deriver\Execution\ResultAssessment;
88use Deriver\Deriver\Identity\QueryEncoding;
89use Deriver\Deriver\Identity\ValueGraph;
90use Deriver\Deriver\Lowerer\AggregateLowering;
91use Deriver\Deriver\Lowerer\AssignmentLowering;
92use Deriver\Deriver\Lowerer\CallLowering;
93use Deriver\Deriver\Lowerer\Control\ConditionalLowering;
94use Deriver\Deriver\Lowerer\Control\DestructuringLowering;
95use Deriver\Deriver\Lowerer\Control\ExceptionLowering;
96use Deriver\Deriver\Lowerer\Declaration\CallableIdentity;
97use Deriver\Deriver\Lowerer\Declaration\CallableSource;
98use Deriver\Deriver\Lowerer\Declaration\Index;
99use Deriver\Deriver\Lowerer\Declaration\Indexer;
100use Deriver\Deriver\Lowerer\Declaration\Metadata\PropertyDeclaration;
101use Deriver\Deriver\Lowerer\Declaration\Traits\Composition;
102use Deriver\Deriver\Lowerer\ExpressionLowering;
103use Deriver\Deriver\Lowerer\Graph\Argument;
104use Deriver\Deriver\Lowerer\Graph\BasicBlock;
105use Deriver\Deriver\Lowerer\Graph\CallableGraph;
106use Deriver\Deriver\Lowerer\Graph\CatchTarget;
107use Deriver\Deriver\Lowerer\Graph\ExceptionRegion;
108use Deriver\Deriver\Lowerer\Graph\Instruction;
109use Deriver\Deriver\Lowerer\Graph\Parameter;
110use Deriver\Deriver\Lowerer\Graph\Terminator;
111use Deriver\Deriver\Lowerer\Hierarchy\Builtins;
112use Deriver\Deriver\Lowerer\Hierarchy\Dispatch;
113use Deriver\Deriver\Lowerer\InstructionEmitter;
114use Deriver\Deriver\Lowerer\Lowerer;
115use Deriver\Deriver\Lowerer\LoweringScope;
116use Deriver\Deriver\Lowerer\StatementLowering;
117use Deriver\Deriver\MicroEvaluatorConversion;
118use Deriver\Deriver\Parser\Node\File;
119use Deriver\Deriver\Parser\Parser;
120use Deriver\Deriver\Parser\ParserConfig;
121use Deriver\Deriver\Parser\TargetSyntax;
122use Deriver\Deriver\Providers\ProviderExpansion;
123use Deriver\Deriver\Resolver\AssignmentPatterns;
124use Deriver\Deriver\Resolver\ClassScope;
125use Deriver\Deriver\Resolver\ExpressionValidation;
126use Deriver\Deriver\Resolver\MagicContext;
127use Deriver\Deriver\Resolver\Resolver;
128use Deriver\Deriver\Selection\QueryValidation;
129use Deriver\Deriver\SnapshotIdentity;
130use Deriver\Deriver\Sources\LineMap;
131use Deriver\Deriver\Sources\Path as SourcesPath;
132use Deriver\Deriver\Sources\Source;
133use Deriver\Deriver\Sources\SourceRef;
134use Deriver\Deriver\Sources\Sources;
135use Deriver\Exception\ParserException;
136use Deriver\Exception\ResolutionException;
137use PHPUnit\Framework\Attributes\CoversClass;
138use PHPUnit\Framework\Attributes\DataProvider;
139use PHPUnit\Framework\Attributes\Small;
140use PHPUnit\Framework\Attributes\UsesClass;
141use PHPUnit\Framework\TestCase;
142
143#[UsesClass(Resolver::class)]
144#[UsesClass(ExpressionValidation::class)]
145#[UsesClass(ResolutionException::class)]
146#[UsesClass(ParserConfig::class)]
147#[UsesClass(ParserException::class)]
148#[CoversClass(MemoryStep::class)]
149#[UsesClass(QueryExecution::class)]
150#[UsesClass(QueryValidation::class)]
151#[UsesClass(ResultAssessment::class)]
152#[UsesClass(Deriver::class)]
153#[UsesClass(Constraints::class)]
154#[UsesClass(Argument::class)]
155#[UsesClass(BasicBlock::class)]
156#[UsesClass(CallableGraph::class)]
157#[UsesClass(CallableIdentity::class)]
158#[UsesClass(CatchTarget::class)]
159#[UsesClass(ExceptionRegion::class)]
160#[UsesClass(Instruction::class)]
161#[UsesClass(Parameter::class)]
162#[UsesClass(PropertyDeclaration::class)]
163#[UsesClass(Terminator::class)]
164#[UsesClass(ArgumentBinding::class)]
165#[UsesClass(ArgumentOrder::class)]
166#[UsesClass(CallExecutor::class)]
167#[UsesClass(CallResolution::class)]
168#[UsesClass(CallableCheck::class)]
169#[UsesClass(Builtins::class)]
170#[UsesClass(Dispatch::class)]
171#[UsesClass(Properties::class)]
172#[UsesClass(Signatures::class)]
173#[UsesClass(ParameterBinding::class)]
174#[UsesClass(PassedArgument::class)]
175#[UsesClass(Arguments::class)]
176#[UsesClass(Modes::class)]
177#[UsesClass(Resolution::class)]
178#[UsesClass(Target::class)]
179#[UsesClass(Transfer::class)]
180#[UsesClass(TypeBinding::class)]
181#[UsesClass(TypeCheck::class)]
182#[UsesClass(Completion::class)]
183#[UsesClass(Context::class)]
184#[UsesClass(ExceptionChain::class)]
185#[UsesClass(ExceptionMatch::class)]
186#[UsesClass(Handler::class)]
187#[UsesClass(ObservationLimit::class)]
188#[UsesClass(StateJoin::class)]
189#[UsesClass(Unwinding::class)]
190#[UsesClass(Discovery::class)]
191#[UsesClass(Dependencies::class)]
192#[UsesClass(InstructionTransfer::class)]
193#[UsesClass(Evaluator::class)]
194#[UsesClass(SlotReference::class)]
195#[UsesClass(ObservationCollector::class)]
196#[UsesClass(Address::class)]
197#[UsesClass(Path::class)]
198#[UsesClass(OffsetTransfer::class)]
199#[UsesClass(Conversions::class)]
200#[UsesClass(ScalarErrors::class)]
201#[UsesClass(State::class)]
202#[UsesClass(Evaluation::class)]
203#[UsesClass(Isolation::class)]
204#[UsesClass(PropertySlot::class)]
205#[UsesClass(PureStep::class)]
206#[UsesClass(ReferenceAssignment::class)]
207#[UsesClass(Location::class)]
208#[UsesClass(Materialization::class)]
209#[UsesClass(Memory::class)]
210#[UsesClass(ReferenceConstraint::class)]
211#[UsesClass(StorageCapture::class)]
212#[UsesClass(Extensions::class)]
213#[UsesClass(ProviderExpansion::class)]
214#[UsesClass(Evaluators::class)]
215#[UsesClass(StateRegistry::class)]
216#[UsesClass(DeriverConfig::class)]
217#[UsesClass(EntryPoint::class)]
218#[UsesClass(Sources::class)]
219#[UsesClass(SourcesPath::class)]
220#[UsesClass(Snapshot::class)]
221#[UsesClass(Source::class)]
222#[UsesClass(TargetProfile::class)]
223#[UsesClass(Budget::class)]
224#[UsesClass(QueryScope::class)]
225#[UsesClass(ReturnQuery::class)]
226#[UsesClass(ResultRef::class)]
227#[UsesClass(SourceRef::class)]
228#[UsesClass(Alternative::class)]
229#[UsesClass(Assessment::class)]
230#[UsesClass(Derivation::class)]
231#[UsesClass(DerivationResult::class)]
232#[UsesClass(Frontier::class)]
233#[UsesClass(QueryEncoding::class)]
234#[UsesClass(ValueGraph::class)]
235#[UsesClass(Statistics::class)]
236#[UsesClass(StorageSnapshot::class)]
237#[UsesClass(Parser::class)]
238#[UsesClass(File::class)]
239#[UsesClass(AggregateLowering::class)]
240#[UsesClass(AssignmentLowering::class)]
241#[UsesClass(CallLowering::class)]
242#[UsesClass(Lowerer::class)]
243#[UsesClass(ConditionalLowering::class)]
244#[UsesClass(DestructuringLowering::class)]
245#[UsesClass(ExceptionLowering::class)]
246#[UsesClass(ExpressionLowering::class)]
247#[UsesClass(InstructionEmitter::class)]
248#[UsesClass(LoweringScope::class)]
249#[UsesClass(StatementLowering::class)]
250#[UsesClass(CallableSource::class)]
251#[UsesClass(Indexer::class)]
252#[UsesClass(Index::class)]
253#[UsesClass(Composition::class)]
254#[UsesClass(LineMap::class)]
255#[UsesClass(MagicContext::class)]
256#[UsesClass(AssignmentPatterns::class)]
257#[UsesClass(ClassScope::class)]
258#[UsesClass(TargetSyntax::class)]
259#[UsesClass(Arithmetic::class)]
260#[UsesClass(Arrays::class)]
261#[UsesClass(Identity::class)]
262#[UsesClass(Increment::class)]
263#[UsesClass(Operations::class)]
264#[UsesClass(Term::class)]
265#[Small]
266final class MemoryStepTest extends TestCase
267{
268    public function testEvaluatePreservesTheSemanticContract(): void
269    {
270        $source = '<?php function replace(string &$x) {$x="changed";} function target() {$x="original";replace($x);return $x;}';
271        $symbol = 'target';
272        $input = new Sources([new Source('fixture.php', $source)]);
273        $configuration = new DeriverConfig();
274        $contributions = new Contributions();
275        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
276        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
277        $contributions1 = $providers->contributions;
278        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
279        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
280        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
281        $snapshotId = $identity->id;
282        $input1 = $providers->input;
283        $parser = new Parser(new ParserConfig($target->version));
284        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
285        $program = new Index($snapshotId, $trees);
286        $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
287        self::assertSame('changed', $result->normalOutcomes[0]->values['return']->native());
288        self::assertSame([], $result->frontiers);
289        self::assertSame([], $result->exceptionalOutcomes);
290    }
291    public function testAddressKeepsTheActualVariableBinding(): void
292    {
293        $state = new State();
294        $instruction = new BoundInstruction('local', 'local', new SourceRef('test', 'fixture.php', 0, 1), 'result', name: 'x');
295        $source = '<?php function target(){return 1;}';
296        $budget = new Budget();
297        $configuration = new DeriverConfig();
298        $contributions = new Contributions();
299        $input = new Sources([new Source('fixture.php', $source)]);
300        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
301        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
302        $contributions1 = $providers->contributions;
303        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
304        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
305        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
306        $snapshotId = $identity->id;
307        $input1 = $providers->input;
308        $parser = new Parser(new ParserConfig($target->version));
309        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
310        $program1 = new Index($snapshotId, $trees);
311        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
312        $snapshotId1 = 'test';
313        $input2 = new Sources([new Source('fixture.php', $source)]);
314        $profile = $execution->configuration->target;
315        $parser1 = new Parser(new ParserConfig($profile->version));
316        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
317        $program = new Index($snapshotId1, $trees1);
318        $location = (new MemoryStep(new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators)))->address($instruction, $state);
319        self::assertSame($state->local('x')->root, $location->root);
320        self::assertSame('x', $location->local);
321    }
322    public function testUninitializedRetainsAWarningForAnUndefinedVariable(): void
323    {
324        $source = '<?php function target(){return 1;}';
325        $budget = new Budget();
326        $configuration = new DeriverConfig();
327        $contributions = new Contributions();
328        $input = new Sources([new Source('fixture.php', $source)]);
329        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
330        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
331        $contributions1 = $providers->contributions;
332        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
333        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
334        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
335        $snapshotId = $identity->id;
336        $input1 = $providers->input;
337        $parser = new Parser(new ParserConfig($target->version));
338        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
339        $program1 = new Index($snapshotId, $trees);
340        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
341        $snapshotId1 = 'test';
342        $input2 = new Sources([new Source('fixture.php', $source)]);
343        $profile = $execution->configuration->target;
344        $parser1 = new Parser(new ParserConfig($profile->version));
345        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
346        $program = new Index($snapshotId1, $trees1);
347        $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
348        $state = new State();
349        $instruction = new BoundInstruction('read', 'read', new SourceRef('test', 'fixture.php', 0, 1), 'result');
350        $value = (new MemoryStep($context))->uninitialized($instruction, $state->local('x'), $state);
351        self::assertNull($value->native());
352        self::assertSame('PHP_WARNING', array_values($context->frontiers)[0]->code);
353    }
354    public function testBindingUnsetsOnlyOneLocalAlias(): void
355    {
356        $source = '<?php function target(){$x=1;$y=&$x;unset($x);$y=2;return [isset($x),$y];}';
357        $symbol = 'target';
358        $input = new Sources([new Source('fixture.php', $source)]);
359        $configuration = new DeriverConfig();
360        $contributions = new Contributions();
361        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
362        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
363        $contributions1 = $providers->contributions;
364        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
365        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
366        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
367        $snapshotId = $identity->id;
368        $input1 = $providers->input;
369        $parser = new Parser(new ParserConfig($target->version));
370        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
371        $program = new Index($snapshotId, $trees);
372        $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
373        self::assertSame([], $result->frontiers);
374        self::assertSame([], $result->exceptionalOutcomes);
375        self::assertCount(1, $result->normalOutcomes);
376        self::assertSame([false, 2], $result->normalOutcomes[0]->values['return']->native());
377    }
378    public function testIncrementedRetainsNoEffectValuesAndRecordsTargetWarnings(): void
379    {
380        $source = '<?php function target(){return 1;}';
381        $budget = new Budget();
382        $configuration = new DeriverConfig();
383        $contributions = new Contributions();
384        $input = new Sources([new Source('fixture.php', $source)]);
385        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
386        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
387        $contributions1 = $providers->contributions;
388        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
389        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
390        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
391        $snapshotId = $identity->id;
392        $input1 = $providers->input;
393        $parser = new Parser(new ParserConfig($target->version));
394        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
395        $program1 = new Index($snapshotId, $trees);
396        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
397        $snapshotId1 = 'test';
398        $input2 = new Sources([new Source('fixture.php', $source)]);
399        $profile = $execution->configuration->target;
400        $parser1 = new Parser(new ParserConfig($profile->version));
401        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
402        $program = new Index($snapshotId1, $trees1);
403        $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
404        $instruction = new BoundInstruction('increment', 'increment', new SourceRef('s', 'x.php', 0, 1), attributes: ['delta' => 1]);
405        $value = (new MemoryStep($context))->incremented(Term::constant(true), $instruction);
406        self::assertTrue($value->literal);
407        self::assertSame('PHP_WARNING', array_values($context->frontiers)[0]->code);
408    }
409    public function testPrepareAddressRejectsAnOccupiedMaximumAppendIndexWithoutOverwritingIt(): void
410    {
411        $source = '<?php function target(){$a=[9223372036854775807=>1];try{$a[]=2;}catch(Error $e){return $a;}return 999;}';
412        $symbol = 'target';
413        $input = new Sources([new Source('fixture.php', $source)]);
414        $configuration = new DeriverConfig();
415        $contributions = new Contributions();
416        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
417        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
418        $contributions1 = $providers->contributions;
419        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
420        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
421        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
422        $snapshotId = $identity->id;
423        $input1 = $providers->input;
424        $parser = new Parser(new ParserConfig($target->version));
425        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
426        $program = new Index($snapshotId, $trees);
427        $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
428        self::assertSame([PHP_INT_MAX => 1], $result->normalOutcomes[0]->values['return']->native());
429        self::assertSame([], $result->frontiers);
430    }
431    public function testEvaluateBindsScriptLocalsToSharedGlobalStorage(): void
432    {
433        $source = new SourceRef('test', 'fixture.php', 0, 1);
434        $state = new State();
435        $state->memory->write($state->local('x'), Term::constant(3));
436        $instruction = new BoundInstruction('local', 'local', $source, 'address', name: 'x');
437        $source1 = '<?php function target(){return 1;}';
438        $budget = new Budget();
439        $configuration = new DeriverConfig();
440        $contributions = new Contributions();
441        $input = new Sources([new Source('fixture.php', $source1)]);
442        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
443        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
444        $contributions1 = $providers->contributions;
445        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
446        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
447        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
448        $snapshotId = $identity->id;
449        $input1 = $providers->input;
450        $parser = new Parser(new ParserConfig($target->version));
451        $trees = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input1, false));
452        $program1 = new Index($snapshotId, $trees);
453        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
454        $snapshotId1 = 'test';
455        $input2 = new Sources([new Source('fixture.php', $source1)]);
456        $profile = $execution->configuration->target;
457        $parser1 = new Parser(new ParserConfig($profile->version));
458        $trees1 = array_map(static fn (Source $source3): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source3->path), $source3->contents, $source3->declarationsOnly))), iterator_to_array($input2, false));
459        $program = new Index($snapshotId1, $trees1);
460        $value = (new MemoryStep(new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators)))->evaluate(new CallableGraph('script:fixture.php', [], [], $source), $instruction, $state);
461        self::assertSame(TermKind::Location, $value->kind);
462        self::assertSame('global:x', $value->literal);
463        self::assertSame('global:x', $state->locals['x']->root);
464        self::assertSame(3, $state->memory->cells['global:x']->native());
465        self::assertSame('global:x', $state->addresses['address']->root);
466    }
467    public function testEvaluateWritesAndReadsTheResolvedAddress(): void
468    {
469        $source = new SourceRef('test', 'fixture.php', 0, 1);
470        $body = new CallableGraph('target', [], [], $source);
471        $state = new State();
472        $state->addresses['address'] = $state->memory->allocate(Term::constant(1));
473        $value = Term::constant(7);
474        $state->registers['value'] = $value;
475        $source1 = '<?php function target(){return 1;}';
476        $budget = new Budget();
477        $configuration = new DeriverConfig();
478        $contributions = new Contributions();
479        $input = new Sources([new Source('fixture.php', $source1)]);
480        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
481        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
482        $contributions1 = $providers->contributions;
483        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
484        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
485        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
486        $snapshotId = $identity->id;
487        $input1 = $providers->input;
488        $parser = new Parser(new ParserConfig($target->version));
489        $trees = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input1, false));
490        $program1 = new Index($snapshotId, $trees);
491        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
492        $snapshotId1 = 'test';
493        $input2 = new Sources([new Source('fixture.php', $source1)]);
494        $profile = $execution->configuration->target;
495        $parser1 = new Parser(new ParserConfig($profile->version));
496        $trees1 = array_map(static fn (Source $source3): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source3->path), $source3->contents, $source3->declarationsOnly))), iterator_to_array($input2, false));
497        $program = new Index($snapshotId1, $trees1);
498        $step = new MemoryStep(new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators));
499        self::assertSame($value, $step->evaluate($body, new BoundInstruction('write', 'write', $source, 'result', ['address', 'value']), $state));
500        self::assertSame($value, $step->evaluate($body, new BoundInstruction('read', 'read', $source, 'result', ['address']), $state));
501        self::assertSame($value, $state->memory->read($state->addresses['address']));
502    }
503    public function testEvaluateSilentReadsPreserveAbsenceWithoutWarnings(): void
504    {
505        $source1 = '<?php function target(){return 1;}';
506        $budget = new Budget();
507        $configuration = new DeriverConfig();
508        $contributions = new Contributions();
509        $input = new Sources([new Source('fixture.php', $source1)]);
510        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
511        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
512        $contributions1 = $providers->contributions;
513        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
514        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
515        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
516        $snapshotId = $identity->id;
517        $input1 = $providers->input;
518        $parser = new Parser(new ParserConfig($target->version));
519        $trees = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input1, false));
520        $program1 = new Index($snapshotId, $trees);
521        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
522        $snapshotId1 = 'test';
523        $input2 = new Sources([new Source('fixture.php', $source1)]);
524        $profile = $execution->configuration->target;
525        $parser1 = new Parser(new ParserConfig($profile->version));
526        $trees1 = array_map(static fn (Source $source3): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source3->path), $source3->contents, $source3->declarationsOnly))), iterator_to_array($input2, false));
527        $program = new Index($snapshotId1, $trees1);
528        $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
529        $source = new SourceRef('test', 'fixture.php', 0, 1);
530        $body = new CallableGraph('target', [], [], $source);
531        $state = new State();
532        $state->addresses['address'] = $state->local('x');
533        $step = new MemoryStep($context);
534        self::assertSame(TermKind::Uninitialized, $step->evaluate($body, new BoundInstruction('read', 'read-silent', $source, 'result', ['address']), $state)->kind);
535        $beforeCount = count($context->frontiers);
536        self::assertNull($step->evaluate($body, new BoundInstruction('read', 'read', $source, 'result', ['address']), $state)->native());
537        self::assertSame(['PHP_WARNING'], array_column(array_values($context->frontiers), 'code'));
538        self::assertSame(0, $beforeCount);
539    }
540    public function testEvaluateReferencesAndAliasesShareWrites(): void
541    {
542        $source = new SourceRef('test', 'fixture.php', 0, 1);
543        $body = new CallableGraph('target', [], [], $source);
544        $state = new State();
545        $state->addresses['x'] = $state->local('x');
546        $state->addresses['y'] = $state->local('y');
547        $state->memory->write($state->addresses['y'], Term::constant(4));
548        $source1 = '<?php function target(){return 1;}';
549        $budget = new Budget();
550        $configuration = new DeriverConfig();
551        $contributions = new Contributions();
552        $input = new Sources([new Source('fixture.php', $source1)]);
553        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
554        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
555        $contributions1 = $providers->contributions;
556        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
557        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
558        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
559        $snapshotId = $identity->id;
560        $input1 = $providers->input;
561        $parser = new Parser(new ParserConfig($target->version));
562        $trees = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input1, false));
563        $program1 = new Index($snapshotId, $trees);
564        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
565        $snapshotId1 = 'test';
566        $input2 = new Sources([new Source('fixture.php', $source1)]);
567        $profile = $execution->configuration->target;
568        $parser1 = new Parser(new ParserConfig($profile->version));
569        $trees1 = array_map(static fn (Source $source3): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source3->path), $source3->contents, $source3->declarationsOnly))), iterator_to_array($input2, false));
570        $program = new Index($snapshotId1, $trees1);
571        $step = new MemoryStep(new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators));
572        self::assertSame(4, $step->evaluate($body, new BoundInstruction('alias', 'alias', $source, 'result', ['x', 'y']), $state)->native());
573        $reference = $step->evaluate($body, new BoundInstruction('reference', 'reference', $source, 'result', ['y']), $state);
574        self::assertSame(TermKind::Cell, $reference->kind);
575        self::assertSame($state->memory->reference($state->local('x')), $reference->literal);
576    }
577    #[DataProvider('providerIncrements')]
578    public function testEvaluateIncrementKeepsPreAndPostValuesSeparate(int $delta, bool $post, int $expected, int $after): void
579    {
580        $source = new SourceRef('test', 'fixture.php', 0, 1);
581        $state = new State();
582        $state->addresses['x'] = $state->memory->allocate(Term::constant(3));
583        $instruction = new BoundInstruction('increment', 'increment', $source, 'result', ['x'], attributes: ['delta' => $delta, 'post' => $post]);
584        $source1 = '<?php function target(){return 1;}';
585        $budget = new Budget();
586        $configuration = new DeriverConfig();
587        $contributions = new Contributions();
588        $input = new Sources([new Source('fixture.php', $source1)]);
589        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
590        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
591        $contributions1 = $providers->contributions;
592        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
593        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
594        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
595        $snapshotId = $identity->id;
596        $input1 = $providers->input;
597        $parser = new Parser(new ParserConfig($target->version));
598        $trees = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input1, false));
599        $program1 = new Index($snapshotId, $trees);
600        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
601        $snapshotId1 = 'test';
602        $input2 = new Sources([new Source('fixture.php', $source1)]);
603        $profile = $execution->configuration->target;
604        $parser1 = new Parser(new ParserConfig($profile->version));
605        $trees1 = array_map(static fn (Source $source3): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source3->path), $source3->contents, $source3->declarationsOnly))), iterator_to_array($input2, false));
606        $program = new Index($snapshotId1, $trees1);
607        $result = (new MemoryStep(new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators)))->evaluate(new CallableGraph('target', [], [], $source), $instruction, $state);
608        self::assertSame($expected, $result->native());
609        self::assertSame($after, $state->memory->read($state->addresses['x'])->native());
610    }
611    /**
612     * @return array<string,array{int,bool,int,int}>
613     */
614    public static function providerIncrements(): array
615    {
616        return ['pre increment' => [1, false, 4, 4], 'post increment' => [1, true, 3, 4], 'pre decrement' => [-1, false, 2, 2], 'post decrement' => [-1, true, 3, 2]];
617    }
618    public function testEvaluateFailedIncrementCannotWriteTheThrowableIntoStorage(): void
619    {
620        $source = new SourceRef('test', 'fixture.php', 0, 1);
621        $state = new State();
622        $value = Term::array([]);
623        $state->addresses['x'] = $state->memory->allocate($value);
624        $instruction = new BoundInstruction('increment', 'increment', $source, 'result', ['x']);
625        $source1 = '<?php function target(){return 1;}';
626        $budget = new Budget();
627        $configuration = new DeriverConfig();
628        $contributions = new Contributions();
629        $input = new Sources([new Source('fixture.php', $source1)]);
630        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
631        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
632        $contributions1 = $providers->contributions;
633        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
634        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
635        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
636        $snapshotId = $identity->id;
637        $input1 = $providers->input;
638        $parser = new Parser(new ParserConfig($target->version));
639        $trees = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input1, false));
640        $program1 = new Index($snapshotId, $trees);
641        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
642        $snapshotId1 = 'test';
643        $input2 = new Sources([new Source('fixture.php', $source1)]);
644        $profile = $execution->configuration->target;
645        $parser1 = new Parser(new ParserConfig($profile->version));
646        $trees1 = array_map(static fn (Source $source3): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source3->path), $source3->contents, $source3->declarationsOnly))), iterator_to_array($input2, false));
647        $program = new Index($snapshotId1, $trees1);
648        $result = (new MemoryStep(new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators)))->evaluate(new CallableGraph('target', [], [], $source), $instruction, $state);
649        self::assertSame('TypeError', $result->literal);
650        self::assertSame($value, $state->memory->read($state->addresses['x']));
651    }
652    public function testAddressResolvesReturnedCellsAndRejectsValues(): void
653    {
654        $source = new SourceRef('test', 'fixture.php', 0, 1);
655        $source1 = '<?php function target(){return 1;}';
656        $budget = new Budget();
657        $configuration = new DeriverConfig();
658        $contributions = new Contributions();
659        $input = new Sources([new Source('fixture.php', $source1)]);
660        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
661        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
662        $contributions1 = $providers->contributions;
663        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
664        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
665        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
666        $snapshotId = $identity->id;
667        $input1 = $providers->input;
668        $parser = new Parser(new ParserConfig($target->version));
669        $trees = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input1, false));
670        $program1 = new Index($snapshotId, $trees);
671        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
672        $snapshotId1 = 'test';
673        $input2 = new Sources([new Source('fixture.php', $source1)]);
674        $profile = $execution->configuration->target;
675        $parser1 = new Parser(new ParserConfig($profile->version));
676        $trees1 = array_map(static fn (Source $source3): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source3->path), $source3->contents, $source3->declarationsOnly))), iterator_to_array($input2, false));
677        $program = new Index($snapshotId1, $trees1);
678        $step = new MemoryStep(new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators));
679        $state = new State();
680        $state->registers = ['cell' => new Term(TermKind::Cell, 'shared'), 'value' => Term::constant(1)];
681        self::assertSame('shared', $step->address(new BoundInstruction('address', 'returned-address', $source, 'result', ['cell']), $state)->root);
682        self::assertTrue($step->address(new BoundInstruction('address', 'returned-address', $source, 'result', ['value']), $state)->unknown);
683        self::assertTrue($step->address(new BoundInstruction('address', 'returned-address', $source), $state)->unknown);
684    }
685    public function testPrepareAddressRetainsOriginalOffsetAndPropertyMetadata(): void
686    {
687        $state = new State();
688        $source = new SourceRef('test', 'fixture.php', 0, 1);
689        $parent = $state->memory->allocate(Term::fromNative(['field' => [1]]));
690        $state->addresses['base'] = new Location($parent->root, ['field']);
691        $key = Term::constant('0');
692        $state->registers['key'] = $key;
693        $property = new PropertySlot(new Term(TermKind::Object, 'box'), 'field', 'Box', null);
694        $state->properties['base'] = $property;
695        $instruction = new BoundInstruction('address', 'element-address', $source, 'result', ['base', 'key']);
696        $source1 = '<?php function target(){return 1;}';
697        $budget = new Budget();
698        $configuration = new DeriverConfig();
699        $contributions = new Contributions();
700        $input = new Sources([new Source('fixture.php', $source1)]);
701        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
702        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
703        $contributions1 = $providers->contributions;
704        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
705        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
706        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
707        $snapshotId = $identity->id;
708        $input1 = $providers->input;
709        $parser = new Parser(new ParserConfig($target->version));
710        $trees = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input1, false));
711        $program1 = new Index($snapshotId, $trees);
712        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
713        $snapshotId1 = 'test';
714        $input2 = new Sources([new Source('fixture.php', $source1)]);
715        $profile = $execution->configuration->target;
716        $parser1 = new Parser(new ParserConfig($profile->version));
717        $trees1 = array_map(static fn (Source $source3): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source3->path), $source3->contents, $source3->declarationsOnly))), iterator_to_array($input2, false));
718        $program = new Index($snapshotId1, $trees1);
719        $step = new MemoryStep(new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators));
720        $value = $step->prepareAddress($instruction, $state);
721        self::assertSame(TermKind::Location, $value->kind);
722        self::assertSame($parent->root, $value->literal);
723        self::assertSame(['field', 0], $state->addresses['result']->path);
724        self::assertSame($property, $state->properties['result']);
725        self::assertSame($key, $state->offsets['result']->key);
726        self::assertSame('base', $state->offsets['result']->parent);
727    }
728    public function testAddressKeepsUnknownKeysAtTheParentLocation(): void
729    {
730        $state = new State();
731        $state->addresses['base'] = new Location('array', ['field']);
732        $state->registers['key'] = Term::parameter('key', 'string');
733        $instruction = new BoundInstruction('address', 'element-address', new SourceRef('test', 'fixture.php', 0, 1), 'result', ['base', 'key']);
734        $source = '<?php function target(){return 1;}';
735        $budget = new Budget();
736        $configuration = new DeriverConfig();
737        $contributions = new Contributions();
738        $input = new Sources([new Source('fixture.php', $source)]);
739        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
740        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
741        $contributions1 = $providers->contributions;
742        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
743        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
744        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
745        $snapshotId = $identity->id;
746        $input1 = $providers->input;
747        $parser = new Parser(new ParserConfig($target->version));
748        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
749        $program1 = new Index($snapshotId, $trees);
750        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
751        $snapshotId1 = 'test';
752        $input2 = new Sources([new Source('fixture.php', $source)]);
753        $profile = $execution->configuration->target;
754        $parser1 = new Parser(new ParserConfig($profile->version));
755        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
756        $program = new Index($snapshotId1, $trees1);
757        $address = (new MemoryStep(new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators)))->address($instruction, $state);
758        self::assertSame('array', $address->root);
759        self::assertSame(['field'], $address->path);
760        self::assertTrue($address->unknown);
761    }
762    public function testPrepareAddressAppendsUsingTheStoredCounter(): void
763    {
764        $state = new State();
765        $state->addresses['base'] = $state->memory->allocate(Term::fromNative([4 => 'a']));
766        $instruction = new BoundInstruction('address', 'element-address', new SourceRef('test', 'fixture.php', 0, 1), 'result', ['base', '']);
767        $source = '<?php function target(){return 1;}';
768        $budget = new Budget();
769        $configuration = new DeriverConfig();
770        $contributions = new Contributions();
771        $input = new Sources([new Source('fixture.php', $source)]);
772        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
773        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
774        $contributions1 = $providers->contributions;
775        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
776        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
777        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
778        $snapshotId = $identity->id;
779        $input1 = $providers->input;
780        $parser = new Parser(new ParserConfig($target->version));
781        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
782        $program1 = new Index($snapshotId, $trees);
783        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
784        $snapshotId1 = 'test';
785        $input2 = new Sources([new Source('fixture.php', $source)]);
786        $profile = $execution->configuration->target;
787        $parser1 = new Parser(new ParserConfig($profile->version));
788        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
789        $program = new Index($snapshotId1, $trees1);
790        (new MemoryStep(new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators)))->prepareAddress($instruction, $state);
791        self::assertSame([5], $state->addresses['result']->path);
792        self::assertNull($state->offsets['result']->key);
793    }
794    public function testUninitializedTypedPropertiesAndThisProduceErrorsWithoutWarnings(): void
795    {
796        $source = '<?php function target(){return 1;}';
797        $budget = new Budget();
798        $configuration = new DeriverConfig();
799        $contributions = new Contributions();
800        $input = new Sources([new Source('fixture.php', $source)]);
801        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
802        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
803        $contributions1 = $providers->contributions;
804        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
805        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
806        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
807        $snapshotId = $identity->id;
808        $input1 = $providers->input;
809        $parser = new Parser(new ParserConfig($target->version));
810        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
811        $program1 = new Index($snapshotId, $trees);
812        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
813        $snapshotId1 = 'test';
814        $input2 = new Sources([new Source('fixture.php', $source)]);
815        $profile = $execution->configuration->target;
816        $parser1 = new Parser(new ParserConfig($profile->version));
817        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
818        $program = new Index($snapshotId1, $trees1);
819        $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
820        $state = new State();
821        $state->memory->cells['typed'] = new Term(TermKind::Uninitialized, attributes: ['type' => 'int']);
822        $instruction = new BoundInstruction('read', 'read', new SourceRef('test', 'fixture.php', 0, 1));
823        $step = new MemoryStep($context);
824        self::assertSame('Error', $step->uninitialized($instruction, new Location('typed'), $state)->literal);
825        self::assertSame('Error', $step->uninitialized($instruction, new Location('missing', local: 'this'), $state)->literal);
826        self::assertSame([], $context->frontiers);
827    }
828    public function testBindingUsesExistingGlobalAndStaticStorageWithoutReinitializing(): void
829    {
830        $source = new SourceRef('test', 'fixture.php', 0, 1);
831        $body = new CallableGraph('target', [], [], $source);
832        $state = new State();
833        $state->memory->cells = ['global:x' => Term::constant(7), 'static:target:y' => Term::constant(8)];
834        $state->registers['initializer'] = Term::constant(99);
835        $source1 = '<?php function target(){return 1;}';
836        $budget = new Budget();
837        $configuration = new DeriverConfig();
838        $contributions = new Contributions();
839        $input = new Sources([new Source('fixture.php', $source1)]);
840        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
841        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
842        $contributions1 = $providers->contributions;
843        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
844        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
845        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
846        $snapshotId = $identity->id;
847        $input1 = $providers->input;
848        $parser = new Parser(new ParserConfig($target->version));
849        $trees = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input1, false));
850        $program1 = new Index($snapshotId, $trees);
851        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
852        $snapshotId1 = 'test';
853        $input2 = new Sources([new Source('fixture.php', $source1)]);
854        $profile = $execution->configuration->target;
855        $parser1 = new Parser(new ParserConfig($profile->version));
856        $trees1 = array_map(static fn (Source $source3): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source3->path), $source3->contents, $source3->declarationsOnly))), iterator_to_array($input2, false));
857        $program = new Index($snapshotId1, $trees1);
858        $step = new MemoryStep(new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators));
859        self::assertNull($step->binding($body, new BoundInstruction('global', 'global', $source), $state, new Location('local-x', local: 'x'))->native());
860        self::assertNull($step->binding($body, new BoundInstruction('static', 'static-local', $source, operands: ['address', 'initializer']), $state, new Location('local-y', local: 'y'))->native());
861        self::assertSame(7, $state->memory->read($state->local('x'))->native());
862        self::assertSame(8, $state->memory->read($state->local('y'))->native());
863        self::assertSame('global:x', $state->local('x')->root);
864        self::assertSame('static:target:y', $state->local('y')->root);
865    }
866    public function testBindingInitializesUnseenStaticsAndKeepsUnknownSharedState(): void
867    {
868        $source = new SourceRef('test', 'fixture.php', 0, 1);
869        $body = new CallableGraph('target', [], [], $source);
870        $state = new State();
871        $location = $state->local('x');
872        $source1 = '<?php function target(){return 1;}';
873        $budget = new Budget();
874        $configuration = new DeriverConfig();
875        $contributions = new Contributions();
876        $input = new Sources([new Source('fixture.php', $source1)]);
877        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
878        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
879        $contributions1 = $providers->contributions;
880        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
881        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
882        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
883        $snapshotId = $identity->id;
884        $input1 = $providers->input;
885        $parser = new Parser(new ParserConfig($target->version));
886        $trees = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input1, false));
887        $program1 = new Index($snapshotId, $trees);
888        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
889        $snapshotId1 = 'test';
890        $input2 = new Sources([new Source('fixture.php', $source1)]);
891        $profile = $execution->configuration->target;
892        $parser1 = new Parser(new ParserConfig($profile->version));
893        $trees1 = array_map(static fn (Source $source3): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source3->path), $source3->contents, $source3->declarationsOnly))), iterator_to_array($input2, false));
894        $program = new Index($snapshotId1, $trees1);
895        $step = new MemoryStep(new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators));
896        $check = new BoundInstruction('check', 'static-initialized', $source);
897        self::assertFalse($step->binding($body, $check, $state, $location)->native());
898        $state->registers['initializer'] = Term::constant(4);
899        $step->binding($body, new BoundInstruction('static', 'static-local', $source, operands: ['address', 'initializer']), $state, $location);
900        self::assertTrue($step->binding($body, $check, $state, $location)->native());
901        self::assertSame(4, $state->memory->read($state->local('x'))->native());
902        $state->memory->unknownShared = 'UNKNOWN_CALL';
903        self::assertTrue($step->binding($body, $check, $state, new Location('unseen', local: 'y'))->native());
904        self::assertSame('UNKNOWN_CALL', $state->memory->read($state->local('y'))->literal);
905    }
906    public function testBindingCreatesExternalGlobalsAndRemovesOnlySelectedArrayElements(): void
907    {
908        $source = new SourceRef('test', 'fixture.php', 0, 1);
909        $body = new CallableGraph('target', [], [], $source);
910        $state = new State();
911        $source1 = '<?php function target(){return 1;}';
912        $budget = new Budget();
913        $configuration = new DeriverConfig();
914        $contributions = new Contributions();
915        $input = new Sources([new Source('fixture.php', $source1)]);
916        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
917        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
918        $contributions1 = $providers->contributions;
919        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
920        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
921        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
922        $snapshotId = $identity->id;
923        $input1 = $providers->input;
924        $parser = new Parser(new ParserConfig($target->version));
925        $trees = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input1, false));
926        $program1 = new Index($snapshotId, $trees);
927        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
928        $snapshotId1 = 'test';
929        $input2 = new Sources([new Source('fixture.php', $source1)]);
930        $profile = $execution->configuration->target;
931        $parser1 = new Parser(new ParserConfig($profile->version));
932        $trees1 = array_map(static fn (Source $source3): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source3->path), $source3->contents, $source3->declarationsOnly))), iterator_to_array($input2, false));
933        $program = new Index($snapshotId1, $trees1);
934        $step = new MemoryStep(new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators));
935        $step->binding($body, new BoundInstruction('global', 'global', $source), $state, new Location('unseen', local: 'x'));
936        self::assertSame(TermKind::External, $state->memory->read($state->local('x'))->kind);
937        self::assertSame('global:x', $state->memory->read($state->local('x'))->literal);
938        $array = $state->memory->allocate(Term::fromNative(['a' => 1, 'b' => 2]));
939        $step->binding($body, new BoundInstruction('unset', 'unset', $source), $state, new Location($array->root, ['a'], local: 'array'));
940        self::assertSame(['b' => 2], $state->memory->read($array)->native());
941    }
942    #[DataProvider('providerTemporaryReferenceDiagnostics')]
943    public function testReturnedAddressAnchorsTemporaryValuesAndReportsOnlyRequiredReferenceNotices(bool $diagnostic): void
944    {
945        $source1 = '<?php function target(){return 1;}';
946        $budget = new Budget();
947        $configuration = new DeriverConfig();
948        $contributions = new Contributions();
949        $input = new Sources([new Source('fixture.php', $source1)]);
950        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
951        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
952        $contributions1 = $providers->contributions;
953        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
954        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
955        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
956        $snapshotId = $identity->id;
957        $input1 = $providers->input;
958        $parser = new Parser(new ParserConfig($target->version));
959        $trees = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input1, false));
960        $program1 = new Index($snapshotId, $trees);
961        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
962        $snapshotId1 = 'test';
963        $input2 = new Sources([new Source('fixture.php', $source1)]);
964        $profile = $execution->configuration->target;
965        $parser1 = new Parser(new ParserConfig($profile->version));
966        $trees1 = array_map(static fn (Source $source3): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source3->path), $source3->contents, $source3->declarationsOnly))), iterator_to_array($input2, false));
967        $program = new Index($snapshotId1, $trees1);
968        $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
969        $state = new State();
970        $value = Term::fromNative([7]);
971        $state->registers['returned'] = $value;
972        $source = new SourceRef('test', 'a.php', 1, 9);
973        $instruction = new BoundInstruction('i', 'returned-address', $source, 'address', ['returned'], attributes: ['temporary-reference' => true, 'temporary-warning' => $diagnostic]);
974        $address = (new MemoryStep($context))->returnedAddress($instruction, $state);
975        self::assertFalse($address->unknown);
976        self::assertSame($value, $state->memory->read($address));
977        self::assertSame($diagnostic ? ['PHP_WARNING'] : [], array_column($context->frontiers, 'code'));
978        self::assertSame($diagnostic ? [$source] : [], array_column($context->frontiers, 'at'));
979        $state->memory->write($address, Term::constant(8));
980        self::assertSame($value, $state->registers['returned']);
981    }
982    /**
983     * @return iterable<string,array{bool}>
984     */
985    public static function providerTemporaryReferenceDiagnostics(): iterable
986    {
987        yield 'assignment from ordinary return' => [true];
988        yield 'foreach temporary' => [false];
989    }
990    public function testReturnedAddressRetainsAnExistingReturnedCellWithoutAReferenceNotice(): void
991    {
992        $source = '<?php function target(){return 1;}';
993        $budget = new Budget();
994        $configuration = new DeriverConfig();
995        $contributions = new Contributions();
996        $input = new Sources([new Source('fixture.php', $source)]);
997        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
998        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
999        $contributions1 = $providers->contributions;
1000        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
1001        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
1002        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
1003        $snapshotId = $identity->id;
1004        $input1 = $providers->input;
1005        $parser = new Parser(new ParserConfig($target->version));
1006        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
1007        $program1 = new Index($snapshotId, $trees);
1008        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
1009        $snapshotId1 = 'test';
1010        $input2 = new Sources([new Source('fixture.php', $source)]);
1011        $profile = $execution->configuration->target;
1012        $parser1 = new Parser(new ParserConfig($profile->version));
1013        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
1014        $program = new Index($snapshotId1, $trees1);
1015        $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
1016        $state = new State();
1017        $original = $state->memory->allocate(Term::fromNative([7]));
1018        $state->registers['returned'] = new Term(TermKind::Cell, $original->root);
1019        $instruction = new BoundInstruction('i', 'returned-address', new SourceRef('test', 'a.php', 1, 9), 'address', ['returned'], attributes: ['temporary-reference' => true]);
1020        $address = (new MemoryStep($context))->returnedAddress($instruction, $state);
1021        self::assertSame($original->root, $address->root);
1022        self::assertFalse($address->unknown);
1023        self::assertSame([], $context->frontiers);
1024        $state->memory->write($address, Term::constant(8));
1025        self::assertSame(8, $state->memory->read($original)->literal);
1026    }
1027    public function testPrepareStorageResolvesAConcreteDynamicLocal(): void
1028    {
1029        $source = '<?php function target(){return 1;}';
1030        $budget = new Budget();
1031        $configuration = new DeriverConfig();
1032        $contributions = new Contributions();
1033        $input = new Sources([new Source('fixture.php', $source)]);
1034        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
1035        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
1036        $contributions1 = $providers->contributions;
1037        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
1038        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
1039        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
1040        $snapshotId = $identity->id;
1041        $input1 = $providers->input;
1042        $parser = new Parser(new ParserConfig($target->version));
1043        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
1044        $program1 = new Index($snapshotId, $trees);
1045        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
1046        $snapshotId1 = 'test';
1047        $input2 = new Sources([new Source('fixture.php', $source)]);
1048        $profile = $execution->configuration->target;
1049        $parser1 = new Parser(new ParserConfig($profile->version));
1050        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
1051        $program = new Index($snapshotId1, $trees1);
1052        $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
1053        $body = $context->program->callable('target');
1054        self::assertNotNull($body);
1055        $state = new State();
1056        $state->registers['name'] = Term::constant('sql');
1057        (new MemoryStep($context))->prepareStorage($body, new BoundInstruction('i', 'dynamic-local', $body->source, 'address', ['name']), $state);
1058        self::assertSame('sql', $state->addresses['address']->local);
1059    }
1060    /**
1061     * @param Location $address Read storage
1062     * @param list<string> $expected Named variable
1063     */
1064    #[DataProvider('providerVariables')]
1065    public function testVariableNamesGlobalAndLocalStorageOnly(Location $address, array $expected): void
1066    {
1067        $source = '<?php function target(){return 1;}';
1068        $budget = new Budget();
1069        $configuration = new DeriverConfig();
1070        $contributions = new Contributions();
1071        $input = new Sources([new Source('fixture.php', $source)]);
1072        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
1073        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
1074        $contributions1 = $providers->contributions;
1075        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
1076        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
1077        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
1078        $snapshotId = $identity->id;
1079        $input1 = $providers->input;
1080        $parser = new Parser(new ParserConfig($target->version));
1081        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
1082        $program1 = new Index($snapshotId, $trees);
1083        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
1084        $snapshotId1 = 'test';
1085        $input2 = new Sources([new Source('fixture.php', $source)]);
1086        $profile = $execution->configuration->target;
1087        $parser1 = new Parser(new ParserConfig($profile->version));
1088        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
1089        $program = new Index($snapshotId1, $trees1);
1090        self::assertSame($expected, (new MemoryStep(new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators)))->variable($address));
1091    }
1092    /**
1093     * @return iterable<string,array{Location,list<string>}>
1094     */
1095    public static function providerVariables(): iterable
1096    {
1097        yield 'local' => [new Location('cell:0', local: 'sql'), ['variable:sql']];
1098        yield 'global' => [new Location('global:sql', local: 'sql'), ['global:sql']];
1099        yield 'element' => [new Location('global:sql', ['k'], 'sql'), []];
1100        yield 'anonymous cell' => [new Location('cell:0'), []];
1101        yield 'unknown' => [new Location('global:sql', local: 'sql', unknown: true), []];
1102    }
1103    public function testUninitializedNamesTheReadVariable(): void
1104    {
1105        $source = '<?php function target(){return 1;}';
1106        $budget = new Budget();
1107        $configuration = new DeriverConfig();
1108        $contributions = new Contributions();
1109        $input = new Sources([new Source('fixture.php', $source)]);
1110        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
1111        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
1112        $contributions1 = $providers->contributions;
1113        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
1114        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
1115        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
1116        $snapshotId = $identity->id;
1117        $input1 = $providers->input;
1118        $parser = new Parser(new ParserConfig($target->version));
1119        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
1120        $program1 = new Index($snapshotId, $trees);
1121        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
1122        $snapshotId1 = 'test';
1123        $input2 = new Sources([new Source('fixture.php', $source)]);
1124        $profile = $execution->configuration->target;
1125        $parser1 = new Parser(new ParserConfig($profile->version));
1126        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
1127        $program = new Index($snapshotId1, $trees1);
1128        $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
1129        $state = new State();
1130        $instruction = new BoundInstruction('read', 'read', new SourceRef('test', 'fixture.php', 0, 1), 'result');
1131        (new MemoryStep($context))->uninitialized($instruction, $state->local('x'), $state);
1132        self::assertSame(['variable:x'], array_values($context->frontiers)[0]->knownDependencies);
1133    }
1134    public function testReadReportsAnUnconfiguredGlobal(): void
1135    {
1136        $source = '<?php function target(){return 1;}';
1137        $budget = new Budget();
1138        $configuration = new DeriverConfig();
1139        $contributions = new Contributions();
1140        $input = new Sources([new Source('fixture.php', $source)]);
1141        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
1142        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
1143        $contributions1 = $providers->contributions;
1144        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
1145        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
1146        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
1147        $snapshotId = $identity->id;
1148        $input1 = $providers->input;
1149        $parser = new Parser(new ParserConfig($target->version));
1150        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
1151        $program1 = new Index($snapshotId, $trees);
1152        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
1153        $snapshotId1 = 'test';
1154        $input2 = new Sources([new Source('fixture.php', $source)]);
1155        $profile = $execution->configuration->target;
1156        $parser1 = new Parser(new ParserConfig($profile->version));
1157        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
1158        $program = new Index($snapshotId1, $trees1);
1159        $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
1160        $state = new State();
1161        $state->memory->cells['global:wpdb'] = new Term(TermKind::External, 'global:wpdb', attributes: ['type' => 'mixed']);
1162        $state->locals['wpdb'] = new Location('global:wpdb');
1163        $state->addresses['a'] = $state->local('wpdb');
1164        $value = (new MemoryStep($context))->evaluate(new CallableGraph('target', [], [], new SourceRef('test', 'fixture.php', 0, 1)), new BoundInstruction('read', 'read', new SourceRef('test', 'fixture.php', 0, 1), 'result', ['a']), $state);
1165        self::assertSame('global:wpdb', $value->literal);
1166        $frontier = array_values($context->frontiers)[0];
1167        self::assertSame(['EXTERNAL_INPUT', 'global-read', ['global:wpdb']], [$frontier->code, $frontier->operation, $frontier->knownDependencies]);
1168    }
1169    #[DataProvider('providerUnsetScopes')]
1170    public function testUnsetKeepsAVariableUndefinedAcrossUnknownSymbolTables(string $symbol, ?string $unknown, ?string $expected): void
1171    {
1172        $state = new State();
1173        $state->unknownLocals = $unknown;
1174        $state->memory->cells['global:x'] = Term::constant(1);
1175        $state->locals['x'] = new Location('global:x');
1176        $source = '<?php function target(){return 1;}';
1177        $budget = new Budget();
1178        $configuration = new DeriverConfig();
1179        $contributions = new Contributions();
1180        $input = new Sources([new Source('fixture.php', $source)]);
1181        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
1182        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
1183        $contributions1 = $providers->contributions;
1184        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
1185        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
1186        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
1187        $snapshotId = $identity->id;
1188        $input1 = $providers->input;
1189        $parser = new Parser(new ParserConfig($target->version));
1190        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
1191        $program1 = new Index($snapshotId, $trees);
1192        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
1193        $snapshotId1 = 'test';
1194        $input2 = new Sources([new Source('fixture.php', $source)]);
1195        $profile = $execution->configuration->target;
1196        $parser1 = new Parser(new ParserConfig($profile->version));
1197        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
1198        $program = new Index($snapshotId1, $trees1);
1199        (new MemoryStep(new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators)))->unset(new CallableGraph($symbol, [], [], new SourceRef('test', 'fixture.php', 0, 1)), $state, $state->local('x'));
1200        self::assertSame($expected, isset($state->locals['x']) ? $state->memory->read($state->locals['x'])->kind->value : null);
1201        self::assertSame(str_starts_with($symbol, 'script:') ? 'uninitialized' : 'constant', $state->memory->cells['global:x']->kind->value);
1202    }
1203    /**
1204     * @return iterable<string,array{string,string|null,string|null}>
1205     */
1206    public static function providerUnsetScopes(): iterable
1207    {
1208        yield 'function' => ['target', null, null];
1209        yield 'function after boundary' => ['target', 'INCLUDE_SEMANTICS_UNSUPPORTED', 'uninitialized'];
1210        yield 'script' => ['script:fixture.php', null, null];
1211    }
1212    public function testUnknownSharedMakesALazilyCreatedGlobalUnknown(): void
1213    {
1214        $source = '<?php function target(){return 1;}';
1215        $budget = new Budget();
1216        $configuration = new DeriverConfig();
1217        $contributions = new Contributions();
1218        $input = new Sources([new Source('fixture.php', $source)]);
1219        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
1220        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
1221        $contributions1 = $providers->contributions;
1222        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
1223        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
1224        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
1225        $snapshotId = $identity->id;
1226        $input1 = $providers->input;
1227        $parser = new Parser(new ParserConfig($target->version));
1228        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
1229        $program1 = new Index($snapshotId, $trees);
1230        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
1231        $snapshotId1 = 'test';
1232        $input2 = new Sources([new Source('fixture.php', $source)]);
1233        $profile = $execution->configuration->target;
1234        $parser1 = new Parser(new ParserConfig($profile->version));
1235        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
1236        $program = new Index($snapshotId1, $trees1);
1237        $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
1238        $state = new State();
1239        $step = new MemoryStep($context);
1240        self::assertNull($step->unknownShared(new State()));
1241        $state->memory->unknownShared = 'MISSING_CALL_MODEL';
1242        $value = $step->unknownShared($state);
1243        self::assertNotNull($value);
1244        self::assertSame(['opaque', 'MISSING_CALL_MODEL', true], [$value->kind->value, $value->literal, $value->attributes['maybeUninitialized'] ?? null]);
1245        $state->addresses['g'] = new Location('global:g', local: 'g');
1246        $step->binding(new CallableGraph('target', [], [], new SourceRef('test', 'fixture.php', 0, 1)), new BoundInstruction('g', 'global', new SourceRef('test', 'fixture.php', 0, 1), 'result', ['g']), $state, $state->addresses['g']);
1247        self::assertSame(TermKind::Opaque, $state->memory->cells['global:g']->kind);
1248    }
1249}
1250