packages/deriver/tests/Unit/Deriver/Evaluator/Execution/Call/Native/InvocationTest.php
1<?php
2
3declare (strict_types=1);
4
5namespace Tests\Unit\Deriver\Evaluator\Execution\Call\Native;
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\Provider\DispatchDecision;
17use Deriver\Deriver\Evaluator\Call\Provider\DispatchRequest;
18use Deriver\Deriver\Evaluator\Call\Registrations;
19use Deriver\Deriver\Evaluator\Call\StateRegistry;
20use Deriver\Deriver\Evaluator\EvaluatorConfig;
21use Deriver\Deriver\Evaluator\Evidence\Frontier;
22use Deriver\Deriver\Evaluator\Execution\Call\Allocation;
23use Deriver\Deriver\Evaluator\Execution\Call\ArgumentBinding;
24use Deriver\Deriver\Evaluator\Execution\Call\ArgumentOrder;
25use Deriver\Deriver\Evaluator\Execution\Call\CallExecutor;
26use Deriver\Deriver\Evaluator\Execution\Call\CallResolution;
27use Deriver\Deriver\Evaluator\Execution\Call\Creation\Access;
28use Deriver\Deriver\Evaluator\Execution\Call\Member\Invocation as MemberInvocation;
29use Deriver\Deriver\Evaluator\Execution\Call\MethodInvocation;
30use Deriver\Deriver\Evaluator\Execution\Call\Native\Invocation;
31use Deriver\Deriver\Evaluator\Execution\Call\Native\Properties;
32use Deriver\Deriver\Evaluator\Execution\Call\Native\Signatures;
33use Deriver\Deriver\Evaluator\Execution\Call\ParameterBinding;
34use Deriver\Deriver\Evaluator\Execution\Call\PassedArgument;
35use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Arguments;
36use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Creation;
37use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Methods;
38use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Modes;
39use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Resolution;
40use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Target;
41use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Transfer;
42use Deriver\Deriver\Evaluator\Execution\Call\ProviderDispatch;
43use Deriver\Deriver\Evaluator\Execution\Call\TypeBinding;
44use Deriver\Deriver\Evaluator\Execution\Call\TypeCheck;
45use Deriver\Deriver\Evaluator\Execution\Call\UnknownCall;
46use Deriver\Deriver\Evaluator\Execution\Completion\Alternative;
47use Deriver\Deriver\Evaluator\Execution\Completion\Assessment;
48use Deriver\Deriver\Evaluator\Execution\Completion\Completion;
49use Deriver\Deriver\Evaluator\Execution\Completion\CompletionKind;
50use Deriver\Deriver\Evaluator\Execution\Completion\StorageSnapshot;
51use Deriver\Deriver\Evaluator\Execution\Constant\ClassNames;
52use Deriver\Deriver\Evaluator\Execution\Context;
53use Deriver\Deriver\Evaluator\Execution\Control\ExceptionChain;
54use Deriver\Deriver\Evaluator\Execution\Control\ExceptionMatch;
55use Deriver\Deriver\Evaluator\Execution\Control\Handler;
56use Deriver\Deriver\Evaluator\Execution\Control\ObservationLimit;
57use Deriver\Deriver\Evaluator\Execution\Control\StateJoin;
58use Deriver\Deriver\Evaluator\Execution\Control\Unwinding;
59use Deriver\Deriver\Evaluator\Execution\Demand\Cell;
60use Deriver\Deriver\Evaluator\Execution\Demand\Components;
61use Deriver\Deriver\Evaluator\Execution\Demand\Discovery;
62use Deriver\Deriver\Evaluator\Execution\Demand\Key;
63use Deriver\Deriver\Evaluator\Execution\Demand\Table;
64use Deriver\Deriver\Evaluator\Execution\Dependencies;
65use Deriver\Deriver\Evaluator\Execution\Evaluator;
66use Deriver\Deriver\Evaluator\Execution\EvaluatorConfig as ExecutionEvaluatorConfig;
67use Deriver\Deriver\Evaluator\Execution\Havoc;
68use Deriver\Deriver\Evaluator\Execution\InstructionTransfer;
69use Deriver\Deriver\Evaluator\Execution\Memory\Location;
70use Deriver\Deriver\Evaluator\Execution\Memory\Materialization;
71use Deriver\Deriver\Evaluator\Execution\Memory\Memory;
72use Deriver\Deriver\Evaluator\Execution\Memory\ReferenceConstraint;
73use Deriver\Deriver\Evaluator\Execution\Memory\Slots\SlotReference;
74use Deriver\Deriver\Evaluator\Execution\Memory\Slots\StateStorage;
75use Deriver\Deriver\Evaluator\Execution\Memory\StorageCapture;
76use Deriver\Deriver\Evaluator\Execution\ObservationCollector;
77use Deriver\Deriver\Evaluator\Execution\Operation\Conversions;
78use Deriver\Deriver\Evaluator\Execution\Operation\ScalarErrors;
79use Deriver\Deriver\Evaluator\Execution\State;
80use Deriver\Deriver\Evaluator\Execution\Summary\CompletionRecord;
81use Deriver\Deriver\Evaluator\Execution\Summary\Evaluation;
82use Deriver\Deriver\Evaluator\Execution\Summary\Invocation as SummaryInvocation;
83use Deriver\Deriver\Evaluator\Execution\Summary\Isolation;
84use Deriver\Deriver\Evaluator\Execution\Trace\Derivation;
85use Deriver\Deriver\Evaluator\Execution\Transfer\CallableTransfer;
86use Deriver\Deriver\Evaluator\Execution\Transfer\ConstantTransfer;
87use Deriver\Deriver\Evaluator\Execution\Transfer\MemoryStep;
88use Deriver\Deriver\Evaluator\Execution\Transfer\ObjectAccess;
89use Deriver\Deriver\Evaluator\Execution\Transfer\PropertyAccessCheck;
90use Deriver\Deriver\Evaluator\Execution\Transfer\PropertyLookup;
91use Deriver\Deriver\Evaluator\Execution\Transfer\PropertyMagic;
92use Deriver\Deriver\Evaluator\Execution\Transfer\PropertySlot;
93use Deriver\Deriver\Evaluator\Execution\Transfer\PropertyTransfer;
94use Deriver\Deriver\Evaluator\Execution\Transfer\PureStep;
95use Deriver\Deriver\Evaluator\Execution\Transfer\ReferenceAssignment;
96use Deriver\Deriver\Evaluator\Request\Budget;
97use Deriver\Deriver\Evaluator\Request\EntryPoint;
98use Deriver\Deriver\Evaluator\Request\QueryScope;
99use Deriver\Deriver\Evaluator\Request\ReturnQuery;
100use Deriver\Deriver\Evaluator\Scalar\Arithmetic;
101use Deriver\Deriver\Evaluator\Scalar\Arrays;
102use Deriver\Deriver\Evaluator\Scalar\Comparison;
103use Deriver\Deriver\Evaluator\Scalar\Identity;
104use Deriver\Deriver\Evaluator\Scalar\NumericString;
105use Deriver\Deriver\Evaluator\Scalar\Operations;
106use Deriver\Deriver\Evaluator\TargetProfile;
107use Deriver\Deriver\Evaluator\Term;
108use Deriver\Deriver\Evaluator\TermKind;
109use Deriver\Deriver\Execution\DerivationResult;
110use Deriver\Deriver\Execution\QueryExecution;
111use Deriver\Deriver\Execution\ResultAssessment;
112use Deriver\Deriver\Identity\QueryEncoding;
113use Deriver\Deriver\Identity\ValueGraph;
114use Deriver\Deriver\Lowerer\AggregateLowering;
115use Deriver\Deriver\Lowerer\AssignmentLowering;
116use Deriver\Deriver\Lowerer\CallLowering;
117use Deriver\Deriver\Lowerer\Control\DestructuringLowering;
118use Deriver\Deriver\Lowerer\Control\ExceptionLowering;
119use Deriver\Deriver\Lowerer\Declaration\CallableIdentity;
120use Deriver\Deriver\Lowerer\Declaration\CallableSource;
121use Deriver\Deriver\Lowerer\Declaration\ConstantSignatures;
122use Deriver\Deriver\Lowerer\Declaration\Index;
123use Deriver\Deriver\Lowerer\Declaration\Indexer;
124use Deriver\Deriver\Lowerer\Declaration\Metadata\ClassDeclaration;
125use Deriver\Deriver\Lowerer\Declaration\Metadata\PropertyDeclaration;
126use Deriver\Deriver\Lowerer\Declaration\Traits\Composition;
127use Deriver\Deriver\Lowerer\EffectInspection;
128use Deriver\Deriver\Lowerer\ExpressionLowering;
129use Deriver\Deriver\Lowerer\Graph\Argument;
130use Deriver\Deriver\Lowerer\Graph\BasicBlock;
131use Deriver\Deriver\Lowerer\Graph\CallableGraph;
132use Deriver\Deriver\Lowerer\Graph\CatchTarget;
133use Deriver\Deriver\Lowerer\Graph\ExceptionRegion;
134use Deriver\Deriver\Lowerer\Graph\Instruction;
135use Deriver\Deriver\Lowerer\Graph\Parameter;
136use Deriver\Deriver\Lowerer\Graph\Terminator;
137use Deriver\Deriver\Lowerer\Hierarchy\Access as HierarchyAccess;
138use Deriver\Deriver\Lowerer\Hierarchy\Builtins;
139use Deriver\Deriver\Lowerer\Hierarchy\Dispatch;
140use Deriver\Deriver\Lowerer\InstructionEmitter;
141use Deriver\Deriver\Lowerer\Lowerer;
142use Deriver\Deriver\Lowerer\LoweringScope;
143use Deriver\Deriver\Lowerer\StatementLowering;
144use Deriver\Deriver\MicroEvaluatorConversion;
145use Deriver\Deriver\Parser\Node\File;
146use Deriver\Deriver\Parser\Parser;
147use Deriver\Deriver\Parser\ParserConfig;
148use Deriver\Deriver\Parser\TargetSyntax;
149use Deriver\Deriver\Providers\ProviderExpansion;
150use Deriver\Deriver\Resolver\AssignmentPatterns;
151use Deriver\Deriver\Resolver\ClassScope;
152use Deriver\Deriver\Resolver\ExpressionValidation;
153use Deriver\Deriver\Resolver\MagicContext;
154use Deriver\Deriver\Resolver\Resolver;
155use Deriver\Deriver\Selection\QueryValidation;
156use Deriver\Deriver\SnapshotIdentity;
157use Deriver\Deriver\Sources\LineMap;
158use Deriver\Deriver\Sources\Path;
159use Deriver\Deriver\Sources\Source;
160use Deriver\Deriver\Sources\SourceRef;
161use Deriver\Deriver\Sources\Sources;
162use Deriver\Exception\ParserException;
163use Deriver\Exception\ResolutionException;
164use JsonException;
165use PHPUnit\Framework\Attributes\CoversClass;
166use PHPUnit\Framework\Attributes\DataProvider;
167use PHPUnit\Framework\Attributes\Small;
168use PHPUnit\Framework\Attributes\UsesClass;
169use PHPUnit\Framework\TestCase;
170
171/**
172 * @covers \Deriver\Deriver\Evaluator\Execution\Call\Native\Invocation
173 */
174#[UsesClass(Resolver::class)]
175#[UsesClass(ExpressionValidation::class)]
176#[UsesClass(ResolutionException::class)]
177#[UsesClass(ParserConfig::class)]
178#[UsesClass(ParserException::class)]
179#[CoversClass(Invocation::class)]
180#[UsesClass(QueryExecution::class)]
181#[UsesClass(QueryValidation::class)]
182#[UsesClass(ResultAssessment::class)]
183#[UsesClass(Deriver::class)]
184#[UsesClass(Argument::class)]
185#[UsesClass(BasicBlock::class)]
186#[UsesClass(CallableGraph::class)]
187#[UsesClass(CallableIdentity::class)]
188#[UsesClass(CatchTarget::class)]
189#[UsesClass(ClassDeclaration::class)]
190#[UsesClass(ExceptionRegion::class)]
191#[UsesClass(Instruction::class)]
192#[UsesClass(Parameter::class)]
193#[UsesClass(PropertyDeclaration::class)]
194#[UsesClass(Terminator::class)]
195#[UsesClass(Allocation::class)]
196#[UsesClass(ArgumentBinding::class)]
197#[UsesClass(ArgumentOrder::class)]
198#[UsesClass(CallExecutor::class)]
199#[UsesClass(CallResolution::class)]
200#[UsesClass(Access::class)]
201#[UsesClass(Builtins::class)]
202#[UsesClass(Dispatch::class)]
203#[UsesClass(HierarchyAccess::class)]
204#[UsesClass(MemberInvocation::class)]
205#[UsesClass(MethodInvocation::class)]
206#[UsesClass(Properties::class)]
207#[UsesClass(Signatures::class)]
208#[UsesClass(ParameterBinding::class)]
209#[UsesClass(PassedArgument::class)]
210#[UsesClass(Arguments::class)]
211#[UsesClass(Creation::class)]
212#[UsesClass(Methods::class)]
213#[UsesClass(Modes::class)]
214#[UsesClass(Resolution::class)]
215#[UsesClass(Target::class)]
216#[UsesClass(Transfer::class)]
217#[UsesClass(ProviderDispatch::class)]
218#[UsesClass(TypeBinding::class)]
219#[UsesClass(TypeCheck::class)]
220#[UsesClass(UnknownCall::class)]
221#[UsesClass(Completion::class)]
222#[UsesClass(ClassNames::class)]
223#[UsesClass(Context::class)]
224#[UsesClass(ExceptionChain::class)]
225#[UsesClass(ExceptionMatch::class)]
226#[UsesClass(Handler::class)]
227#[UsesClass(ObservationLimit::class)]
228#[UsesClass(StateJoin::class)]
229#[UsesClass(Unwinding::class)]
230#[UsesClass(Cell::class)]
231#[UsesClass(Components::class)]
232#[UsesClass(Discovery::class)]
233#[UsesClass(Key::class)]
234#[UsesClass(Table::class)]
235#[UsesClass(Dependencies::class)]
236#[UsesClass(Havoc::class)]
237#[UsesClass(InstructionTransfer::class)]
238#[UsesClass(Evaluator::class)]
239#[UsesClass(SlotReference::class)]
240#[UsesClass(StateStorage::class)]
241#[UsesClass(ObservationCollector::class)]
242#[UsesClass(Conversions::class)]
243#[UsesClass(ScalarErrors::class)]
244#[UsesClass(State::class)]
245#[UsesClass(CompletionRecord::class)]
246#[UsesClass(Evaluation::class)]
247#[UsesClass(SummaryInvocation::class)]
248#[UsesClass(Isolation::class)]
249#[UsesClass(CallableTransfer::class)]
250#[UsesClass(ConstantTransfer::class)]
251#[UsesClass(MemoryStep::class)]
252#[UsesClass(ObjectAccess::class)]
253#[UsesClass(PropertyAccessCheck::class)]
254#[UsesClass(PropertyLookup::class)]
255#[UsesClass(PropertyMagic::class)]
256#[UsesClass(PropertySlot::class)]
257#[UsesClass(PropertyTransfer::class)]
258#[UsesClass(PureStep::class)]
259#[UsesClass(ReferenceAssignment::class)]
260#[UsesClass(Location::class)]
261#[UsesClass(Materialization::class)]
262#[UsesClass(Memory::class)]
263#[UsesClass(ReferenceConstraint::class)]
264#[UsesClass(StorageCapture::class)]
265#[UsesClass(DispatchDecision::class)]
266#[UsesClass(DispatchRequest::class)]
267#[UsesClass(Extensions::class)]
268#[UsesClass(ProviderExpansion::class)]
269#[UsesClass(Evaluators::class)]
270#[UsesClass(StateRegistry::class)]
271#[UsesClass(DeriverConfig::class)]
272#[UsesClass(EntryPoint::class)]
273#[UsesClass(Sources::class)]
274#[UsesClass(Path::class)]
275#[UsesClass(Snapshot::class)]
276#[UsesClass(Source::class)]
277#[UsesClass(TargetProfile::class)]
278#[UsesClass(Budget::class)]
279#[UsesClass(QueryScope::class)]
280#[UsesClass(ReturnQuery::class)]
281#[UsesClass(ResultRef::class)]
282#[UsesClass(SourceRef::class)]
283#[UsesClass(Alternative::class)]
284#[UsesClass(Assessment::class)]
285#[UsesClass(Derivation::class)]
286#[UsesClass(DerivationResult::class)]
287#[UsesClass(Frontier::class)]
288#[UsesClass(QueryEncoding::class)]
289#[UsesClass(ValueGraph::class)]
290#[UsesClass(Statistics::class)]
291#[UsesClass(StorageSnapshot::class)]
292#[UsesClass(Parser::class)]
293#[UsesClass(File::class)]
294#[UsesClass(AggregateLowering::class)]
295#[UsesClass(AssignmentLowering::class)]
296#[UsesClass(CallLowering::class)]
297#[UsesClass(Lowerer::class)]
298#[UsesClass(DestructuringLowering::class)]
299#[UsesClass(ExceptionLowering::class)]
300#[UsesClass(EffectInspection::class)]
301#[UsesClass(ExpressionLowering::class)]
302#[UsesClass(InstructionEmitter::class)]
303#[UsesClass(LoweringScope::class)]
304#[UsesClass(StatementLowering::class)]
305#[UsesClass(ConstantSignatures::class)]
306#[UsesClass(CallableSource::class)]
307#[UsesClass(Indexer::class)]
308#[UsesClass(Index::class)]
309#[UsesClass(Composition::class)]
310#[UsesClass(LineMap::class)]
311#[UsesClass(MagicContext::class)]
312#[UsesClass(AssignmentPatterns::class)]
313#[UsesClass(ClassScope::class)]
314#[UsesClass(TargetSyntax::class)]
315#[UsesClass(Arithmetic::class)]
316#[UsesClass(Arrays::class)]
317#[UsesClass(Comparison::class)]
318#[UsesClass(Identity::class)]
319#[UsesClass(NumericString::class)]
320#[UsesClass(Operations::class)]
321#[UsesClass(Term::class)]
322#[Small]
323final class InvocationTest extends TestCase
324{
325 public function testApplyChecksInternalConstructorTypes(): void
326 {
327 $source = '<?php function target(){try{new Exception(previous:new stdClass);}catch(TypeError $e){return "type";}return 999;}';
328 $symbol = 'target';
329 $input = new Sources([new Source('fixture.php', $source)]);
330 $configuration = new DeriverConfig();
331 $contributions = new Contributions();
332 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
333 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
334 $contributions1 = $providers->contributions;
335 $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);
336 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
337 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
338 $snapshotId = $identity->id;
339 $input1 = $providers->input;
340 $parser = new Parser(new ParserConfig($target->version));
341 $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
342 $program = new Index($snapshotId, $trees);
343 $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
344 self::assertSame('type', $result->normalOutcomes[0]->values['return']->native());
345 self::assertSame([], $result->frontiers);
346 }
347 public function testCoercionsAppliesNullConversionOnlyInWeakNativeCalls(): void
348 {
349 $source = '<?php function target(){return 1;}';
350 $budget = new Budget();
351 $configuration = new DeriverConfig();
352 $contributions = new Contributions();
353 $input = new Sources([new Source('fixture.php', $source)]);
354 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
355 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
356 $contributions1 = $providers->contributions;
357 $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);
358 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
359 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
360 $snapshotId = $identity->id;
361 $input1 = $providers->input;
362 $parser = new Parser(new ParserConfig($target->version));
363 $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
364 $program1 = new Index($snapshotId, $trees);
365 $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
366 $snapshotId1 = 'test';
367 $input2 = new Sources([new Source('fixture.php', $source)]);
368 $profile = $execution->configuration->target;
369 $parser1 = new Parser(new ParserConfig($profile->version));
370 $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(Path::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
371 $program = new Index($snapshotId1, $trees1);
372 $invocation = new Invocation(new Evaluator(new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators)));
373 $source3 = '<?php function target(){return 1;}';
374 $budget1 = new Budget();
375 $configuration1 = new DeriverConfig();
376 $contributions2 = new Contributions();
377 $input3 = new Sources([new Source('fixture.php', $source3)]);
378 $target1 = new TargetProfile($configuration1->phpVersion, $configuration1->integerBits, $configuration1->floatPrecision);
379 $providers1 = new ProviderExpansion($input3, array_map(Term::fromNative(...), $configuration1->environment), $contributions2);
380 $contributions3 = $providers1->contributions;
381 $evaluators1 = new Evaluators(new TargetProfile($configuration1->phpVersion, $configuration1->integerBits, $configuration1->floatPrecision), new Registrations($contributions3->evaluators, $contributions3->providers, $contributions3->domains, $contributions3->intrinsics, $contributions3->stateSlots), $configuration1->standardEvaluators);
382 $settings1 = new EvaluatorConfig($target1, $providers1->environment, $configuration1->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration1->evaluators));
383 $identity1 = new SnapshotIdentity($providers1->input, $settings1, $evaluators1, $providers1->manifest);
384 $snapshotId2 = $identity1->id;
385 $input4 = $providers1->input;
386 $parser2 = new Parser(new ParserConfig($target1->version));
387 $trees2 = array_map(static fn (Source $source4): File => (new Resolver())->resolve($parser2->parse(new Source(Path::normalize($source4->path), $source4->contents, $source4->declarationsOnly))), iterator_to_array($input4, false));
388 $program3 = new Index($snapshotId2, $trees2);
389 $execution1 = new QueryExecution($program3, new ExecutionEvaluatorConfig($target1, $providers1->environment, $configuration1->standardEvaluators), $evaluators1, $identity1->snapshot(array_map(Frontier::fromDiagnostic(...), $program3->diagnostics())));
390 $snapshotId3 = 'test';
391 $input5 = new Sources([new Source('fixture.php', $source3)]);
392 $profile1 = $execution1->configuration->target;
393 $parser3 = new Parser(new ParserConfig($profile1->version));
394 $trees3 = array_map(static fn (Source $source5): File => (new Resolver())->resolve($parser3->parse(new Source(Path::normalize($source5->path), $source5->contents, $source5->declarationsOnly))), iterator_to_array($input5, false));
395 $program2 = new Index($snapshotId3, $trees3);
396 $signature = (new Signatures((new Context($program2, new ReturnQuery('target', budget: $budget1), $execution1->configuration, $execution1->evaluators))->program))->graph('Exception', '__construct', new SourceRef('s', 'x.php', 0, 1));
397 $actuals = [new PassedArgument(Term::constant(null), name: 'message')];
398 self::assertSame('', $invocation->coercions($signature, $actuals, false)[0]->value->literal);
399 self::assertNull($invocation->coercions($signature, $actuals, true)[0]->value->literal);
400 }
401 public function testInitializeRetainsInheritedNonzeroCodeWhenPassedZero(): void
402 {
403 $source = '<?php class X extends Exception{protected $message="old";protected $code=9;}function target(){$e=new X(code:0);return [$e->getMessage(),$e->getCode()];}';
404 $symbol = 'target';
405 $input = new Sources([new Source('fixture.php', $source)]);
406 $configuration = new DeriverConfig();
407 $contributions = new Contributions();
408 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
409 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
410 $contributions1 = $providers->contributions;
411 $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);
412 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
413 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
414 $snapshotId = $identity->id;
415 $input1 = $providers->input;
416 $parser = new Parser(new ParserConfig($target->version));
417 $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
418 $program = new Index($snapshotId, $trees);
419 $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
420 self::assertSame(['', 9], $result->normalOutcomes[0]->values['return']->native());
421 self::assertSame([], $result->frontiers);
422 }
423 public function testAssignedPreservesPreviousAndNormalizesLineDefault(): void
424 {
425 $source = '<?php function target(){return 1;}';
426 $budget = new Budget();
427 $configuration = new DeriverConfig();
428 $contributions = new Contributions();
429 $input = new Sources([new Source('fixture.php', $source)]);
430 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
431 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
432 $contributions1 = $providers->contributions;
433 $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);
434 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
435 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
436 $snapshotId = $identity->id;
437 $input1 = $providers->input;
438 $parser = new Parser(new ParserConfig($target->version));
439 $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
440 $program1 = new Index($snapshotId, $trees);
441 $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
442 $snapshotId1 = 'test';
443 $input2 = new Sources([new Source('fixture.php', $source)]);
444 $profile = $execution->configuration->target;
445 $parser1 = new Parser(new ParserConfig($profile->version));
446 $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(Path::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
447 $program = new Index($snapshotId1, $trees1);
448 $invocation = new Invocation(new Evaluator(new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators)));
449 $entry = new State();
450 $entry->memory->write($entry->local('filename'), Term::constant('chosen.php'));
451 self::assertNull($invocation->assigned('previous', Term::constant(null), $entry));
452 self::assertSame(0, $invocation->assigned('line', Term::constant(null), $entry)?->literal);
453 self::assertNull($invocation->assigned('code', Term::constant(0), $entry));
454 }
455 #[DataProvider('providerNativeCoercions')]
456 public function testCoercionsUsesParameterPositionAndNameWithoutChangingReferenceMetadata(?string $name, int $position, Term $input, Term $expected): void
457 {
458 $source = '<?php function target(){return 1;}';
459 $budget = new Budget();
460 $configuration = new DeriverConfig();
461 $contributions = new Contributions();
462 $input1 = new Sources([new Source('fixture.php', $source)]);
463 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
464 $providers = new ProviderExpansion($input1, array_map(Term::fromNative(...), $configuration->environment), $contributions);
465 $contributions1 = $providers->contributions;
466 $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);
467 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
468 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
469 $snapshotId = $identity->id;
470 $input2 = $providers->input;
471 $parser = new Parser(new ParserConfig($target->version));
472 $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input2, false));
473 $program1 = new Index($snapshotId, $trees);
474 $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
475 $snapshotId1 = 'test';
476 $input3 = new Sources([new Source('fixture.php', $source)]);
477 $profile = $execution->configuration->target;
478 $parser1 = new Parser(new ParserConfig($profile->version));
479 $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(Path::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input3, false));
480 $program = new Index($snapshotId1, $trees1);
481 $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
482 $signature = (new Signatures($context->program))->graph('ErrorException', '__construct', new SourceRef('s', 'a.php', 0, 1));
483 $location = new Location('argument', ['key']);
484 $argument = new PassedArgument($input, $name, $location);
485 $arguments = array_fill(0, $position, new PassedArgument(Term::constant(1)));
486 $arguments[] = $argument;
487 $result = (new Invocation(new Evaluator($context)))->coercions($signature, $arguments, false);
488 self::assertSame($expected->kind->value, $result[$position]->value->kind->value);
489 self::assertSame($expected->literal, $result[$position]->value->literal);
490 self::assertSame($name, $result[$position]->name);
491 self::assertSame($location, $result[$position]->location);
492 self::assertCount($position + 1, $result);
493 }
494 /**
495 * @return iterable<string,array{?string,int,Term,Term}>
496 */
497 public static function providerNativeCoercions(): iterable
498 {
499 yield 'positional message' => [null, 0, Term::constant(null), Term::constant('')];
500 yield 'positional code' => [null, 1, Term::constant(null), Term::constant(0)];
501 yield 'positional severity' => [null, 2, Term::constant(null), Term::constant(0)];
502 yield 'nullable filename' => [null, 3, Term::constant(null), Term::constant(null)];
503 yield 'nullable line' => [null, 4, Term::constant(null), Term::constant(null)];
504 yield 'named message' => ['message', 0, Term::constant(null), Term::constant('')];
505 yield 'named code' => ['code', 0, Term::constant(null), Term::constant(0)];
506 yield 'unknown parameter' => ['missing', 0, Term::constant(null), Term::constant(null)];
507 yield 'extra positional' => [null, 6, Term::constant(null), Term::constant(null)];
508 yield 'false unchanged' => ['message', 0, Term::constant(false), Term::constant(false)];
509 yield 'symbolic unchanged' => ['message', 0, Term::parameter('x'), Term::parameter('x')];
510 }
511 #[DataProvider('providerAssignedFields')]
512 public function testAssignedFollowsNativeFieldRetentionAndFilenameRules(string $name, Term $value, Term $filename, ?Term $expected): void
513 {
514 $source = '<?php function target(){return 1;}';
515 $budget = new Budget();
516 $configuration = new DeriverConfig();
517 $contributions = new Contributions();
518 $input = new Sources([new Source('fixture.php', $source)]);
519 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
520 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
521 $contributions1 = $providers->contributions;
522 $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);
523 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
524 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
525 $snapshotId = $identity->id;
526 $input1 = $providers->input;
527 $parser = new Parser(new ParserConfig($target->version));
528 $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
529 $program1 = new Index($snapshotId, $trees);
530 $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
531 $snapshotId1 = 'test';
532 $input2 = new Sources([new Source('fixture.php', $source)]);
533 $profile = $execution->configuration->target;
534 $parser1 = new Parser(new ParserConfig($profile->version));
535 $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(Path::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
536 $program = new Index($snapshotId1, $trees1);
537 $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
538 $entry = new State();
539 $entry->memory->write($entry->local('filename'), $filename);
540 $result = (new Invocation(new Evaluator($context)))->assigned($name, $value, $entry);
541 self::assertEquals($expected, $result);
542 }
543 /**
544 * @return iterable<string,array{string,Term,Term,?Term}>
545 */
546 public static function providerAssignedFields(): iterable
547 {
548 $null = Term::constant(null);
549 yield 'zero code' => ['code', Term::constant(0), $null, null];
550 yield 'nonzero code' => ['code', Term::constant(7), $null, Term::constant(7)];
551 yield 'symbolic code' => ['code', Term::parameter('n', 'int'), $null, Term::parameter('n', 'int')];
552 yield 'null previous' => ['previous', $null, $null, null];
553 yield 'null filename' => ['filename', $null, $null, null];
554 yield 'null line no file' => ['line', $null, $null, null];
555 yield 'null line chosen file' => ['line', $null, Term::constant('chosen.php'), Term::constant(0)];
556 yield 'null line unknown file' => ['line', $null, Term::parameter('file', 'string|null'), Term::opaque('RUNTIME_STACK', 'int')];
557 yield 'explicit line' => ['line', Term::constant(42), $null, Term::constant(42)];
558 yield 'empty message' => ['message', Term::constant(''), $null, Term::constant('')];
559 }
560 public function testApplyDeclinesUnknownNativeClassesAndMethods(): void
561 {
562 $source = '<?php function target(){return 1;}';
563 $budget = new Budget();
564 $configuration = new DeriverConfig();
565 $contributions = new Contributions();
566 $input = new Sources([new Source('fixture.php', $source)]);
567 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
568 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
569 $contributions1 = $providers->contributions;
570 $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);
571 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
572 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
573 $snapshotId = $identity->id;
574 $input1 = $providers->input;
575 $parser = new Parser(new ParserConfig($target->version));
576 $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
577 $program1 = new Index($snapshotId, $trees);
578 $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
579 $snapshotId1 = 'test';
580 $input2 = new Sources([new Source('fixture.php', $source)]);
581 $profile = $execution->configuration->target;
582 $parser1 = new Parser(new ParserConfig($profile->version));
583 $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(Path::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
584 $program = new Index($snapshotId1, $trees1);
585 $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
586 $invocation = new Invocation(new Evaluator($context));
587 $instruction = new BoundInstruction('i', 'method', new SourceRef('s', 'a.php', 0, 1), 'result');
588 $receiver = new Term(TermKind::Object, 'e', attributes: ['class' => 'Exception']);
589 self::assertNull($invocation->apply('UnknownClass', 'run', [], new State(), $instruction, null, false));
590 self::assertNull($invocation->apply('Exception', 'missing', [], new State(), $instruction, $receiver, false));
591 self::assertSame([], $context->frontiers);
592 }
593 #[DataProvider('providerInvalidNativeReceivers')]
594 public function testApplyRejectsCloningAndIncompatibleReceivers(string $method, ?Term $receiver): void
595 {
596 $source = '<?php function target(){return 1;}';
597 $budget = new Budget();
598 $configuration = new DeriverConfig();
599 $contributions = new Contributions();
600 $input = new Sources([new Source('fixture.php', $source)]);
601 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
602 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
603 $contributions1 = $providers->contributions;
604 $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);
605 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
606 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
607 $snapshotId = $identity->id;
608 $input1 = $providers->input;
609 $parser = new Parser(new ParserConfig($target->version));
610 $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
611 $program1 = new Index($snapshotId, $trees);
612 $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
613 $snapshotId1 = 'test';
614 $input2 = new Sources([new Source('fixture.php', $source)]);
615 $profile = $execution->configuration->target;
616 $parser1 = new Parser(new ParserConfig($profile->version));
617 $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(Path::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
618 $program = new Index($snapshotId1, $trees1);
619 $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
620 $instruction = new BoundInstruction('i', 'method', new SourceRef('s', 'a.php', 0, 1), 'result');
621 $paths = (new Invocation(new Evaluator($context)))->apply('Exception', $method, [], new State(), $instruction, $receiver, false);
622 self::assertNotNull($paths);
623 self::assertCount(1, $paths);
624 self::assertSame(CompletionKind::Throw, $paths[0]->completion->kind);
625 self::assertSame('Error', $paths[0]->completion->value?->literal);
626 self::assertArrayNotHasKey('result', $paths[0]->registers);
627 }
628 /**
629 * @return iterable<string,array{string,?Term}>
630 */
631 public static function providerInvalidNativeReceivers(): iterable
632 {
633 yield 'clone' => ['__clone', new Term(TermKind::Object, 'e', attributes: ['class' => 'Exception'])];
634 yield 'missing receiver' => ['getMessage', null];
635 yield 'wrong class' => ['getMessage', new Term(TermKind::Object, 'e', attributes: ['class' => 'stdClass'])];
636 }
637 #[DataProvider('providerNativeResidualMethods')]
638 public function testApplyKeepsRuntimeStackAndSerializationMethodsBehindABoundary(string $method): void
639 {
640 $source = '<?php function target(){return 1;}';
641 $budget = new Budget();
642 $configuration = new DeriverConfig();
643 $contributions = new Contributions();
644 $input = new Sources([new Source('fixture.php', $source)]);
645 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
646 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
647 $contributions1 = $providers->contributions;
648 $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);
649 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
650 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
651 $snapshotId = $identity->id;
652 $input1 = $providers->input;
653 $parser = new Parser(new ParserConfig($target->version));
654 $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
655 $program1 = new Index($snapshotId, $trees);
656 $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
657 $snapshotId1 = 'test';
658 $input2 = new Sources([new Source('fixture.php', $source)]);
659 $profile = $execution->configuration->target;
660 $parser1 = new Parser(new ParserConfig($profile->version));
661 $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(Path::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
662 $program = new Index($snapshotId1, $trees1);
663 $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
664 $instruction = new BoundInstruction('i', 'method', new SourceRef('s', 'a.php', 0, 1), 'result');
665 $receiver = new Term(TermKind::Object, 'e', attributes: ['class' => 'Exception']);
666 $paths = (new Invocation(new Evaluator($context)))->apply('Exception', $method, [], new State(), $instruction, $receiver, false);
667 self::assertNotNull($paths);
668 self::assertSame(['normal', 'throw'], array_map(static fn (State $path): string => $path->completion->kind->value, $paths));
669 self::assertSame('MISSING_CALL_MODEL', array_values($context->frontiers)[0]->code);
670 }
671 /**
672 * @return iterable<string,array{string}>
673 */
674 public static function providerNativeResidualMethods(): iterable
675 {
676 yield 'trace' => ['getTrace'];
677 yield 'trace string' => ['getTraceAsString'];
678 yield 'string conversion' => ['__toString'];
679 yield 'wake up' => ['__wakeup'];
680 }
681 /**
682 * @throws JsonException If fixture metadata cannot be encoded
683 */
684 #[DataProvider('providerNativePrograms')]
685 public function testApplyPreservesOracleCheckedNativeConstructorsAndGetters(string $source, string $expected): void
686 {
687 $symbol = 'target';
688 $input = new Sources([new Source('fixture.php', $source)]);
689 $configuration = new DeriverConfig();
690 $contributions = new Contributions();
691 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
692 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
693 $contributions1 = $providers->contributions;
694 $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);
695 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
696 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
697 $snapshotId = $identity->id;
698 $input1 = $providers->input;
699 $parser = new Parser(new ParserConfig($target->version));
700 $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
701 $program = new Index($snapshotId, $trees);
702 $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
703 self::assertSame([], $result->frontiers);
704 self::assertSame([], $result->exceptionalOutcomes);
705 self::assertCount(1, $result->normalOutcomes);
706 self::assertSame(json_decode($expected, true, flags: JSON_THROW_ON_ERROR), $result->normalOutcomes[0]->values['return']->native());
707 }
708 /**
709 * @return iterable<string,array{string,string}>
710 */
711 public static function providerNativePrograms(): iterable
712 {
713 return ['native:messageCode' => ['<?php function target(){$e=new Exception("hello",7);return [$e->getMessage(),$e->getCode(),$e->getPrevious()];}', '["hello",7,null]'], 'native:named' => ['<?php function target(){$e=new RuntimeException(code:7,message:"hello");return [$e->getMessage(),$e->getCode()];}', '["hello",7]'], 'native:inherited' => ['<?php class X extends Exception{}function target(){$e=new X(code:7,message:"hello");return [$e->getMessage(),$e->getCode()];}', '["hello",7]'], 'native:codeZero' => ['<?php class X extends Exception{protected $message="x";protected $code=9;}function target(){$a=new X;$b=new X(code:0);$c=new X(message:"a");return [$a->getMessage(),$a->getCode(),$b->getMessage(),$b->getCode(),$c->getMessage(),$c->getCode()];}', '["x",9,"",9,"a",9]'], 'native:previous' => ['<?php function target(){$p=new Exception("first");$e=new Exception(previous:$p);$previous=$e->getPrevious();$equal=$previous===$p;return [$equal,$previous->getMessage()];}', '[true,"first"]'], 'native:parent' => ['<?php class X extends Exception{function __construct(){parent::__construct("a",2);}}function target(){$e=new X;return [$e->getMessage(),$e->getCode()];}', '["a",2]'], 'native:clone' => ['<?php function target(){try{$x=clone new Exception;}catch(Error $e){return "error";}return 999;}', '"error"'], 'native:arrayMessage' => ['<?php function target(){try{new Exception([]);}catch(TypeError $e){return "type";}return 999;}', '"type"'], 'native:badCode' => ['<?php function target(){try{new Exception(code:"no");}catch(TypeError $e){return "type";}return 999;}', '"type"'], 'native:badPrevious' => ['<?php function target(){try{new Exception(previous:new stdClass);}catch(TypeError $e){return "type";}return 999;}', '"type"'], 'native:unknownNamed' => ['<?php function target(){try{new Exception(other:1);}catch(Error $e){return "name";}return 999;}', '"name"'], 'native:extra' => ['<?php function target(){try{new Exception("",0,null,1);}catch(ArgumentCountError $e){return "count";}return 999;}', '"count"'], 'native:strict' => ['<?php declare(strict_types=1);function target(){try{new Exception(1);}catch(TypeError $e){return "type";}return 999;}', '"type"'], 'native:weak' => ['<?php function target(){$e=new Exception(1,"2");return [$e->getMessage(),$e->getCode()];}', '["1",2]'], 'native:errorDefaults' => ['<?php class X extends ErrorException{protected $message="x";protected $code=9;protected int $severity=2;}function target(){$x=new X;return [$x->getMessage(),$x->getCode(),$x->getSeverity()];}', '["x",9,1]'], 'native:repeatPrevious' => ['<?php class X extends Exception{function clear(){parent::__construct();}function reset(){parent::__construct(previous:null);}}function target(){$p=new Exception;$x=new X("x",9,$p);$x->clear();$previous=$x->getPrevious();$a=[$x->getMessage(),$x->getCode(),$previous===$p];$x->reset();$previous=$x->getPrevious();return [$a,$x->getMessage(),$x->getCode(),$previous===$p];}', '[["x",9,true],"",9,true]'], 'native:severity' => ['<?php function target(){$x=new ErrorException(severity:8);return [$x->getMessage(),$x->getCode(),$x->getSeverity()];}', '["",0,8]'], 'native:getterArity' => ['<?php function target(){try{(new Exception)->getMessage(1);}catch(ArgumentCountError $e){return "count";}return 999;}', '"count"'], 'native:protected' => ['<?php function target(){try{return (new Exception("a"))->message;}catch(Error $e){return "access";}}', '"access"'], 'native:sourceProperty' => ['<?php class X extends Exception{function change(){$this->message=42;$this->code="custom";}}function target(){$e=new X;$e->change();return [$e->getMessage(),$e->getCode()];}', '["42","custom"]'], 'native:caseInsensitive' => ['<?php function target(){try{throw new runtimeexception;}catch(EXCEPTION $e){return "caught";}}', '"caught"'], 'native:errorHierarchy' => ['<?php function target(){$e=new DivisionByZeroError("zero");return [$e instanceof ArithmeticError,$e instanceof Throwable,$e->getMessage()];}', '[true,true,"zero"]'], 'native:missingMethod' => ['<?php function target(){try{(new Exception)->absent();}catch(Error $e){return "missing";}return 999;}', '"missing"'], 'native:filename' => ['<?php function target(){$e=new ErrorException(filename:"chosen.php");return [$e->getFile(),$e->getLine()];}', '["chosen.php",0]'], 'native:line' => ['<?php function target(){$e=new ErrorException(filename:"chosen.php",line:42);return [$e->getFile(),$e->getLine()];}', '["chosen.php",42]']];
714 }
715}
716