Passed
Pull Request — 0.9.x (#308)
by Shinji
02:20
created

collectClassDefinitionPointer()

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 10
nc 2
nop 6
dl 0
loc 19
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * This file is part of the reliforp/reli-prof package.
5
 *
6
 * (c) sji <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Reli\Lib\PhpProcessReader\PhpMemoryReader;
15
16
use Reli\Inspector\Settings\TargetPhpSettings\TargetPhpSettings;
17
use Reli\Lib\PhpInternals\Types\Zend\Bucket;
0 ignored issues
show
Bug introduced by
The type Reli\Lib\PhpInternals\Types\Zend\Bucket was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
18
use Reli\Lib\PhpInternals\Types\Zend\ZendArray;
0 ignored issues
show
Bug introduced by
The type Reli\Lib\PhpInternals\Types\Zend\ZendArray was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
19
use Reli\Lib\PhpInternals\Types\Zend\ZendCastedTypeProvider;
20
use Reli\Lib\PhpInternals\Types\Zend\ZendClassConstant;
21
use Reli\Lib\PhpInternals\Types\Zend\ZendClassEntry;
22
use Reli\Lib\PhpInternals\Types\Zend\ZendClosure;
23
use Reli\Lib\PhpInternals\Types\Zend\ZendCompilerGlobals;
24
use Reli\Lib\PhpInternals\Types\Zend\ZendConstant;
25
use Reli\Lib\PhpInternals\Types\Zend\ZendExecutorGlobals;
26
use Reli\Lib\PhpInternals\Types\Zend\ZendFunction;
27
use Reli\Lib\PhpInternals\Types\Zend\ZendMmChunk;
28
use Reli\Lib\PhpInternals\Types\Zend\ZendObject;
29
use Reli\Lib\PhpInternals\Types\Zend\ZendObjectsStore;
30
use Reli\Lib\PhpInternals\Types\Zend\ZendReference;
31
use Reli\Lib\PhpInternals\Types\Zend\ZendResource;
32
use Reli\Lib\PhpInternals\Types\Zend\ZendString;
33
use Reli\Lib\PhpInternals\Types\Zend\Zval;
34
use Reli\Lib\PhpInternals\ZendTypeReader;
35
use Reli\Lib\PhpInternals\ZendTypeReaderCreator;
36
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\CallFrameHeaderMemoryLocation;
37
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\CallFrameVariableTableMemoryLocation;
38
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\DefaultPropertiesTableMemoryLocation;
39
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\DefaultStaticMembersTableMemoryLocation;
40
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\DynamicFuncDefsTableMemoryLocation;
41
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\LocalVariableNameTableMemoryLocation;
42
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\MemoryLocations;
43
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ObjectsStoreMemoryLocation;
44
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ReferenceCountAnalyzer;
45
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\RuntimeCacheMemoryLocation;
46
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\StaticMembersTableMemoryLocation;
47
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\VmStackMemoryLocation;
48
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendArenaMemoryLocation;
49
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendArgInfosMemoryLocation;
50
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendArrayMemoryLocation;
51
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendArrayTableMemoryLocation;
52
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendArrayTableOverheadMemoryLocation;
53
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendClassConstantMemoryLocation;
54
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendClassEntryMemoryLocation;
55
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendConstantMemoryLocation;
56
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendMmChunkMemoryLocation;
57
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendMmHugeListMemoryLocation;
58
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendObjectHandlersMemoryLocation;
59
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendObjectMemoryLocation;
60
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendOpArrayBodyMemoryLocation;
61
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendOpArrayHeaderMemoryLocation;
62
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendPropertyInfoMemoryLocation;
63
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendReferenceMemoryLocation;
64
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendResourceMemoryLocation;
65
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendStringMemoryLocation;
66
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ArgInfoContext;
67
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ArgInfosContext;
68
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ArrayContextPool;
69
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ArrayElementContext;
70
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ArrayElementsContext;
71
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ArrayHeaderContext;
72
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ArrayPossibleOverheadContext;
73
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\CallFrameContext;
74
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\CallFramesContext;
75
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\CallFrameVariableTableContext;
76
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClassConstantContext;
77
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClassConstantInfoContext;
78
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClassConstantsContext;
79
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClassDefinitionContext;
80
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClassEntryContext;
81
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClassStaticPropertiesContext;
82
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClosureContext;
83
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\DefaultPropertiesTableContext;
84
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\DefaultStaticPropertiesContext;
85
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\DefinedClassesContext;
86
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\DefinedFunctionsContext;
87
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\DynamicFuncDefsContext;
88
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\FunctionDefinitionContext;
89
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\GlobalConstantContext;
90
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\GlobalConstantsContext;
91
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\GlobalVariablesContext;
92
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\IncludedFilesContext;
93
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\InternalFunctionDefinitionContext;
94
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\LocalVariableNameTableContext;
95
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ObjectContext;
96
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ObjectContextPool;
97
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ObjectsStoreContext;
98
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\OpArrayContext;
99
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\PhpReferenceContext;
100
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\PhpReferenceContextPool;
101
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\PropertiesInfoContext;
102
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\PropertyInfoContext;
103
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ReferenceContext;
104
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ResourceContext;
105
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\RuntimeCacheContext;
106
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ScalarValueContext;
107
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\StringContext;
108
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\StringContextPool;
109
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\TopReferenceContext;
110
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\UserFunctionDefinitionContext;
111
use Reli\Lib\PhpProcessReader\PhpZendMemoryManagerChunkFinder;
112
use Reli\Lib\Process\MemoryReader\MemoryReaderInterface;
113
use Reli\Lib\Process\Pointer\Dereferencer;
114
use Reli\Lib\Process\Pointer\PointedTypeResolver;
115
use Reli\Lib\Process\Pointer\Pointer;
116
use Reli\Lib\Process\Pointer\RemoteProcessDereferencer;
117
use Reli\Lib\Process\ProcessSpecifier;
118
119
/** @psalm-import-type VersionDecided from TargetPhpSettings */
120
final class MemoryLocationsCollector
121
{
122
    private ?ZendTypeReader $zend_type_reader = null;
123
124
    public function __construct(
125
        private MemoryReaderInterface $memory_reader,
126
        private ZendTypeReaderCreator $zend_type_reader_creator,
127
        private PhpZendMemoryManagerChunkFinder $chunk_finder,
128
    ) {
129
    }
130
131
    /**
132
     * @param value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> $php_version
0 ignored issues
show
Documentation Bug introduced by
The doc comment value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> at position 0 could not be parsed: Unknown type name 'value-of' at position 0 in value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS>.
Loading history...
133
     */
134
    public function getTypeReader(string $php_version): ZendTypeReader
135
    {
136
        if (is_null($this->zend_type_reader)) {
137
            $this->zend_type_reader = $this->zend_type_reader_creator->create($php_version);
138
        }
139
        return $this->zend_type_reader;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->zend_type_reader could return the type null which is incompatible with the type-hinted return Reli\Lib\PhpInternals\ZendTypeReader. Consider adding an additional type-check to rule them out.
Loading history...
140
    }
141
142
    /**
143
     * @param value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> $php_version
0 ignored issues
show
Documentation Bug introduced by
The doc comment value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> at position 0 could not be parsed: Unknown type name 'value-of' at position 0 in value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS>.
Loading history...
144
     */
145
    private function getDereferencer(int $pid, string $php_version): Dereferencer
146
    {
147
        return new RemoteProcessDereferencer(
148
            $this->memory_reader,
149
            new ProcessSpecifier($pid),
150
            new ZendCastedTypeProvider(
151
                $this->getTypeReader($php_version),
152
            ),
153
            new class ($php_version) implements PointedTypeResolver {
154
                public function __construct(
155
                    private string $php_version,
156
                ) {
157
                }
158
159
                public function resolve(string $type_name): string
160
                {
161
                    return match ($this->php_version) {
162
                        ZendTypeReader::V70,
163
                        ZendTypeReader::V71,
164
                        ZendTypeReader::V72 => match ($type_name) {
165
                            Bucket::class => \Reli\Lib\PhpInternals\Types\Zend\V70\Bucket::class,
166
                            ZendArray::class => \Reli\Lib\PhpInternals\Types\Zend\V70\ZendArray::class,
167
                            Zval::class => \Reli\Lib\PhpInternals\Types\Zend\V70\Zval::class,
168
                            default => $type_name,
169
                        },
170
                        ZendTypeReader::V73 => match ($type_name) {
171
                            Bucket::class => \Reli\Lib\PhpInternals\Types\Zend\V73\Bucket::class,
172
                            ZendArray::class => \Reli\Lib\PhpInternals\Types\Zend\V73\ZendArray::class,
173
                            Zval::class => \Reli\Lib\PhpInternals\Types\Zend\V73\Zval::class,
174
                            default => $type_name,
175
                        },
176
                        ZendTypeReader::V74 => match ($type_name) {
177
                            Bucket::class => \Reli\Lib\PhpInternals\Types\Zend\V74\Bucket::class,
178
                            ZendArray::class => \Reli\Lib\PhpInternals\Types\Zend\V74\ZendArray::class,
179
                            Zval::class => \Reli\Lib\PhpInternals\Types\Zend\V74\Zval::class,
180
                            default => $type_name,
181
                        },
182
                        ZendTypeReader::V80,
183
                        ZendTypeReader::V81 => match ($type_name) {
184
                            ZendArray::class => \Reli\Lib\PhpInternals\Types\Zend\V80\ZendArray::class,
185
                            default => $type_name,
186
                        },
187
                        ZendTypeReader::V82 => $type_name,
188
                    };
189
                }
190
            }
191
        );
192
    }
193
194
    /** @param TargetPhpSettings<VersionDecided> $target_php_settings */
195
    private function getMainChunkAddress(
196
        ProcessSpecifier $process_specifier,
197
        TargetPhpSettings $target_php_settings,
198
        Dereferencer $dereferencer,
199
    ): int {
200
        $chunk_address = $this->chunk_finder->findAddress(
201
            $process_specifier,
202
            $target_php_settings,
203
            $dereferencer,
204
        );
205
        if (is_null($chunk_address)) {
206
            throw new \RuntimeException('chunk address not found');
207
        }
208
        return $chunk_address;
209
    }
210
211
    /** @param TargetPhpSettings<VersionDecided> $target_php_settings */
212
    public function collectAll(
213
        ProcessSpecifier $process_specifier,
214
        TargetPhpSettings $target_php_settings,
215
        int $eg_address,
216
        int $cg_address,
217
    ): CollectedMemories {
218
        $pid = $process_specifier->pid;
219
        $php_version = $target_php_settings->php_version;
0 ignored issues
show
Documentation Bug introduced by
It seems like $target_php_settings->php_version of type string is incompatible with the declared type Reli\Inspector\Settings\TargetPhpSettings\TVersion of property $php_version.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
220
        $dereferencer = $this->getDereferencer($pid, $php_version);
221
        $zend_type_reader = $this->zend_type_reader_creator->create($php_version);
222
223
        $main_chunk_header_pointer = new Pointer(
224
            ZendMmChunk::class,
225
            $this->getMainChunkAddress(
226
                $process_specifier,
227
                $target_php_settings,
228
                $dereferencer,
229
            ),
230
            $zend_type_reader->sizeOf('zend_mm_chunk'),
231
        );
232
233
        $memory_locations = new MemoryLocations();
234
        $chunk_memory_locations = new MemoryLocations();
235
236
        $zend_mm_main_chunk = $dereferencer->deref($main_chunk_header_pointer);
237
        foreach ($zend_mm_main_chunk->iterateChunks($dereferencer) as $chunk) {
238
            $chunk_memory_location = ZendMmChunkMemoryLocation::fromZendMmChunk($chunk);
239
            $chunk_memory_locations->add(
240
                $chunk_memory_location
241
            );
242
        }
243
        $huge_memory_locations = new MemoryLocations();
244
        foreach ($zend_mm_main_chunk->heap_slot->iterateHugeList($dereferencer) as $huge_list) {
245
            $huge_memory_locations->add(
246
                ZendMmChunkMemoryLocation::fromZendMmHugeList($huge_list)
247
            );
248
            $memory_locations->add(
249
                ZendMmHugeListMemoryLocation::fromZendMmHugeList($huge_list)
250
            );
251
        }
252
253
        $memory_get_usage_size = $zend_mm_main_chunk->heap_slot->size;
254
        $memory_get_usage_real_size = $zend_mm_main_chunk->heap_slot->real_size;
255
        $cached_chunks_size = $zend_mm_main_chunk->heap_slot->cached_chunks_count * ZendMmChunk::SIZE;
256
257
        $eg_pointer = new Pointer(
258
            ZendExecutorGlobals::class,
259
            $eg_address,
260
            $zend_type_reader->sizeOf('zend_executor_globals')
261
        );
262
        $cg_pointer = new Pointer(
263
            ZendCompilerGlobals::class,
264
            $cg_address,
265
            $zend_type_reader->sizeOf('zend_compiler_globals')
266
        );
267
268
        $compiler_arena_memory_locations = new MemoryLocations();
269
        /** @var ZendCompilerGlobals $cg */
270
        $cg = $dereferencer->deref($cg_pointer);
271
        if ($cg->arena !== null) {
272
            $arena_root = $dereferencer->deref($cg->arena);
273
            foreach ($arena_root->iterateChain($dereferencer) as $arena) {
274
                $compiler_arena_memory_locations->add(
275
                    ZendArenaMemoryLocation::fromZendArena($arena)
276
                );
277
            }
278
        }
279
280
        if ($cg->ast_arena !== null) {
281
            $ast_arena_root = $dereferencer->deref($cg->ast_arena);
282
            foreach ($ast_arena_root->iterateChain($dereferencer) as $ast_arena) {
283
                $compiler_arena_memory_locations->add(
284
                    ZendArenaMemoryLocation::fromZendArena($ast_arena)
285
                );
286
            }
287
        }
288
289
        /** @var ZendExecutorGlobals $eg */
290
        $eg = $dereferencer->deref($eg_pointer);
291
292
        $vm_stack_memory_locations = new MemoryLocations();
293
        if (!is_null($eg->vm_stack)) {
294
            $vm_stack_curent = $dereferencer->deref($eg->vm_stack);
295
            foreach ($vm_stack_curent->iterateStackChain($dereferencer) as $vm_stack) {
296
                $vm_stack_memory_locations->add(
297
                    VmStackMemoryLocation::fromZendVmStack($vm_stack),
298
                );
299
            }
300
        }
301
302
        $context_pools = ContextPools::createDefault();
303
304
        $included_files_context = $this->collectIncludedFiles(
305
            $eg->included_files,
306
            $dereferencer,
307
            $memory_locations,
308
            $context_pools,
309
        );
310
311
        $interned_strings_context = $this->collectInternedStrings(
312
            $cg->interned_strings,
313
            $cg->map_ptr_base,
314
            $dereferencer,
315
            $zend_type_reader,
316
            $memory_locations,
317
            $context_pools,
318
        );
319
320
        assert(!is_null($eg->function_table));
321
        assert(!is_null($eg->class_table));
322
        assert(!is_null($eg->zend_constants));
323
324
        $function_table = $dereferencer->deref($eg->function_table);
325
        $class_table = $dereferencer->deref($eg->class_table);
326
        $zend_constants = $dereferencer->deref($eg->zend_constants);
327
328
        $global_variables_context = $this->collectGlobalVariables(
329
            $eg->symbol_table,
330
            $cg->map_ptr_base,
331
            $dereferencer,
332
            $zend_type_reader,
333
            $memory_locations,
334
            $context_pools,
335
        );
336
337
        $call_frames_context = $this->collectCallFrames(
338
            $eg,
339
            $cg->map_ptr_base,
340
            $dereferencer,
341
            $zend_type_reader,
342
            $memory_locations,
343
            $context_pools,
344
        );
345
346
        $defined_functions_context = $this->collectFunctionTable(
347
            $function_table,
348
            $cg->map_ptr_base,
349
            $dereferencer,
350
            $zend_type_reader,
351
            $memory_locations,
352
            $context_pools,
353
        );
354
355
        $defined_classes_context = $this->collectClassTable(
356
            $class_table,
357
            $cg->map_ptr_base,
358
            $dereferencer,
359
            $zend_type_reader,
360
            $memory_locations,
361
            $context_pools,
362
        );
363
364
        $global_constants_context = $this->collectGlobalConstants(
365
            $zend_constants,
366
            $cg->map_ptr_base,
367
            $dereferencer,
368
            $zend_type_reader,
369
            $memory_locations,
370
            $context_pools,
371
        );
372
373
        $objects_store_context = $this->collectObjectsStore(
374
            $eg->objects_store,
375
            $cg->map_ptr_base,
376
            $dereferencer,
377
            $zend_type_reader,
378
            $memory_locations,
379
            $context_pools,
380
        );
381
382
        $top_reference_context = new TopReferenceContext(
383
            $call_frames_context,
384
            $global_variables_context,
385
            $defined_functions_context,
386
            $defined_classes_context,
387
            $global_constants_context,
388
            $included_files_context,
389
            $interned_strings_context,
390
            $objects_store_context,
391
        );
392
393
        return new CollectedMemories(
394
            $chunk_memory_locations,
395
            $huge_memory_locations,
396
            $vm_stack_memory_locations,
397
            $compiler_arena_memory_locations,
398
            $cached_chunks_size,
399
            $memory_locations,
400
            $top_reference_context,
401
            $memory_get_usage_size,
402
            $memory_get_usage_real_size,
403
        );
404
    }
405
406
    public function collectZval(
407
        Zval $zval,
408
        int $map_ptr_base,
409
        Dereferencer $dereferencer,
410
        ZendTypeReader $zend_type_reader,
411
        MemoryLocations $memory_locations,
412
        ContextPools $context_pools
413
    ): ?ReferenceContext {
414
        if ($zval->isArray()) {
415
            assert(!is_null($zval->value->arr));
416
            return $this->collectZendArrayPointer(
417
                $zval->value->arr,
418
                $map_ptr_base,
419
                $memory_locations,
420
                $dereferencer,
421
                $zend_type_reader,
422
                $context_pools,
423
            );
424
        } elseif ($zval->isObject()) {
425
            assert(!is_null($zval->value->obj));
426
            return $this->collectZendObjectPointer(
427
                $zval->value->obj,
428
                $map_ptr_base,
429
                $memory_locations,
430
                $dereferencer,
431
                $zend_type_reader,
432
                $context_pools,
433
            );
434
        } elseif ($zval->isString()) {
435
            assert(!is_null($zval->value->str));
436
            return $this->collectZendStringPointer(
437
                $zval->value->str,
438
                $memory_locations,
439
                $dereferencer,
440
                $context_pools,
441
            );
442
        } elseif (
443
            $zval->isBool()
444
            or $zval->isLong()
445
            or $zval->isDouble()
446
            or $zval->isNull()
447
        ) {
448
            return match ($zval->getType()) {
449
                'IS_TRUE', 'IS_FALSE'
450
                    => new ScalarValueContext((bool)$zval->value->lval),
451
                'IS_LONG' => new ScalarValueContext($zval->value->lval),
452
                'IS_DOUBLE' => new ScalarValueContext($zval->value->dval),
453
                'IS_NULL' => new ScalarValueContext(null),
454
            };
455
        } elseif ($zval->isReference()) {
456
            assert(!is_null($zval->value->ref));
457
            return $this->collectPhpReferencePointer(
458
                $zval->value->ref,
459
                $map_ptr_base,
460
                $memory_locations,
461
                $dereferencer,
462
                $zend_type_reader,
463
                $context_pools,
464
            );
465
        } elseif ($zval->isResource()) {
466
            assert(!is_null($zval->value->res));
467
            return $this->collectResourcePointer(
468
                $zval->value->res,
469
                $memory_locations,
470
                $dereferencer,
471
                $context_pools,
472
            );
473
        } elseif ($zval->isIndirect()) {
474
            $zval = $dereferencer->deref(
475
                $zval->value->getAsPointer(Zval::class, $zend_type_reader->sizeOf('zval'))
476
            );
477
            return $this->collectZval(
478
                $zval,
479
                $map_ptr_base,
480
                $dereferencer,
481
                $zend_type_reader,
482
                $memory_locations,
483
                $context_pools,
484
            );
485
        }
486
        return null;
487
    }
488
489
    /** @param Pointer<ZendResource> $pointer */
490
    public function collectResourcePointer(
491
        Pointer $pointer,
492
        MemoryLocations $memory_locations,
493
        Dereferencer $dereferencer,
494
        ContextPools $context_pools
495
    ): ResourceContext {
496
        if ($memory_locations->has($pointer->address)) {
497
            $memory_location = $memory_locations->get($pointer->address);
498
            if ($memory_location instanceof ZendResourceMemoryLocation) {
499
                return $context_pools
500
                    ->resource_context_pool
501
                    ->getContextForLocation($memory_location)
502
                ;
503
            }
504
        }
505
        $resource = $dereferencer->deref($pointer);
506
        $memory_location = ZendResourceMemoryLocation::fromZendReference($resource);
507
        $memory_locations->add($memory_location);
508
        return $context_pools
509
            ->resource_context_pool
510
            ->getContextForLocation($memory_location)
511
        ;
512
    }
513
514
515
    /** @param Pointer<ZendReference> $pointer */
516
    public function collectPhpReferencePointer(
517
        Pointer $pointer,
518
        int $map_ptr_base,
519
        MemoryLocations $memory_locations,
520
        Dereferencer $dereferencer,
521
        ZendTypeReader $zend_type_reader,
522
        ContextPools $context_pools
523
    ): PhpReferenceContext {
524
        if ($memory_locations->has($pointer->address)) {
525
            $memory_location = $memory_locations->get($pointer->address);
526
            if ($memory_location instanceof ZendReferenceMemoryLocation) {
527
                return $context_pools
528
                    ->php_reference_context_pool
529
                    ->getContextForLocation($memory_location)
530
                ;
531
            }
532
        }
533
        $php_reference = $dereferencer->deref($pointer);
534
        $memory_location = ZendReferenceMemoryLocation::fromZendReference($php_reference);
535
        $memory_locations->add($memory_location);
536
        $php_referencecontext = $context_pools
537
            ->php_reference_context_pool
538
            ->getContextForLocation($memory_location)
539
        ;
540
        $zval_context = $this->collectZval(
541
            $php_reference->val,
542
            $map_ptr_base,
543
            $dereferencer,
544
            $zend_type_reader,
545
            $memory_locations,
546
            $context_pools,
547
        );
548
        if (!is_null($zval_context)) {
549
            $php_referencecontext->add('referenced', $zval_context);
550
        }
551
        return $php_referencecontext;
552
    }
553
554
    /** @param Pointer<ZendArray> $pointer */
555
    public function collectZendArrayPointer(
556
        Pointer $pointer,
557
        int $map_ptr_base,
558
        MemoryLocations $memory_locations,
559
        Dereferencer $dereferencer,
560
        ZendTypeReader $zend_type_reader,
561
        ContextPools $context_pools
562
    ): ArrayHeaderContext {
563
        if ($memory_locations->has($pointer->address)) {
564
            $memory_location = $memory_locations->get($pointer->address);
565
            if ($memory_location instanceof ZendArrayMemoryLocation) {
566
                return $context_pools
567
                    ->array_context_pool
568
                    ->getContextForLocation($memory_location)
569
                ;
570
            }
571
        }
572
        $array = $dereferencer->deref($pointer);
573
        return $this->collectZendArray(
574
            $array,
575
            $map_ptr_base,
576
            $dereferencer,
577
            $zend_type_reader,
578
            $memory_locations,
579
            $context_pools,
580
        );
581
    }
582
583
    /** @param Pointer<ZendObject> $pointer */
584
    public function collectZendObjectPointer(
585
        Pointer $pointer,
586
        int $map_ptr_base,
587
        MemoryLocations $memory_locations,
588
        Dereferencer $dereferencer,
589
        ZendTypeReader $zend_type_reader,
590
        ContextPools $context_pools
591
    ): ObjectContext {
592
        if ($memory_locations->has($pointer->address)) {
593
            $memory_location = $memory_locations->get($pointer->address);
594
            if ($memory_location instanceof ZendArrayTableOverheadMemoryLocation) {
595
                unset($memory_location);
596
            } else {
597
                assert($memory_location instanceof ZendObjectMemoryLocation);
598
                return $context_pools
599
                    ->object_context_pool
600
                    ->getContextForLocation($memory_location)
601
                    ;
602
            }
603
        }
604
        $obj = $dereferencer->deref($pointer);
605
        return $this->collectZendObject(
606
            $obj,
607
            $map_ptr_base,
608
            $dereferencer,
609
            $zend_type_reader,
610
            $memory_locations,
611
            $context_pools,
612
        );
613
    }
614
615
    /** @param Pointer<ZendString> $pointer */
616
    public function collectZendStringPointer(
617
        Pointer $pointer,
618
        MemoryLocations $memory_locations,
619
        Dereferencer $dereferencer,
620
        ContextPools $context_pools
621
    ): StringContext {
622
        if ($memory_locations->has($pointer->address)) {
623
            $memory_location = $memory_locations->get($pointer->address);
624
            if ($memory_location instanceof ZendArrayTableOverheadMemoryLocation) {
625
                $memory_location = null;
626
            } else {
627
                assert($memory_location instanceof ZendStringMemoryLocation);
628
            }
629
        }
630
        if (!isset($memory_location)) {
631
            $str = $dereferencer->deref($pointer);
632
            $memory_location = ZendStringMemoryLocation::fromZendString(
633
                $str,
634
                $dereferencer,
635
            );
636
            $memory_locations->add($memory_location);
637
        }
638
        assert($memory_location instanceof ZendStringMemoryLocation);
639
        return $context_pools
640
            ->string_context_pool
641
            ->getContextForLocation($memory_location)
642
        ;
643
    }
644
645
    public function collectCallFrames(
646
        ZendExecutorGlobals $eg,
647
        int $map_ptr_base,
648
        Dereferencer $dereferencer,
649
        ZendTypeReader $zend_type_reader,
650
        MemoryLocations $memory_locations,
651
        ContextPools $context_pools
652
    ): CallFramesContext {
653
        $call_frames_context = new CallFramesContext();
654
        if (is_null($eg->current_execute_data)) {
655
            return $call_frames_context;
656
        }
657
        $execute_data = $dereferencer->deref($eg->current_execute_data);
658
        foreach ($execute_data->iterateStackChain($dereferencer) as $key => $execute_data) {
659
            if (is_null($execute_data->func)) {
660
                continue;
661
            }
662
            $function_name = $execute_data->getFunctionName($dereferencer);
663
            $call_frame_context = new CallFrameContext(
664
                $function_name ?? '<main>',
665
            );
666
            $call_frames_context->add((string)$key, $call_frame_context);
667
            $header_memory_location = CallFrameHeaderMemoryLocation::fromZendExecuteData(
668
                $execute_data,
669
            );
670
            $variable_table_memory_location = CallFrameVariableTableMemoryLocation::fromZendExecuteData(
671
                $execute_data,
672
                $dereferencer
673
            );
674
            $memory_locations->add($variable_table_memory_location);
675
            $memory_locations->add($header_memory_location);
676
677
            if ($execute_data->hasThis()) {
678
                $this_context = $this->collectZval(
679
                    $execute_data->This,
680
                    $map_ptr_base,
681
                    $dereferencer,
682
                    $zend_type_reader,
683
                    $memory_locations,
684
                    $context_pools,
685
                );
686
                if (!is_null($this_context)) {
687
                    $call_frame_context->add('this', $this_context);
688
                }
689
            }
690
691
            $has_local_variables = false;
692
            $variable_table_context = new CallFrameVariableTableContext();
693
            $local_variables_iterator = $execute_data->getVariables($dereferencer, $zend_type_reader);
694
            foreach ($local_variables_iterator as $name => $value) {
695
                $local_variable_context = $this->collectZval(
696
                    $value,
697
                    $map_ptr_base,
698
                    $dereferencer,
699
                    $zend_type_reader,
700
                    $memory_locations,
701
                    $context_pools,
702
                );
703
                if (!is_null($local_variable_context)) {
704
                    $variable_table_context->add($name, $local_variable_context);
705
                    $has_local_variables = true;
706
                }
707
            }
708
            if ($has_local_variables) {
709
                $call_frame_context->add('local_variables', $variable_table_context);
710
            }
711
712
            if ($execute_data->hasSymbolTable() and !is_null($execute_data->symbol_table)) {
713
                $symbol_table_context = $this->collectZendArrayPointer(
714
                    $execute_data->symbol_table,
715
                    $map_ptr_base,
716
                    $memory_locations,
717
                    $dereferencer,
718
                    $zend_type_reader,
719
                    $context_pools,
720
                );
721
                $call_frame_context->add('symbol_table', $symbol_table_context);
722
            }
723
            if ($execute_data->hasExtraNamedParams() and !is_null($execute_data->extra_named_params)) {
724
                $extra_named_params_context = $this->collectZendArrayPointer(
725
                    $execute_data->extra_named_params,
726
                    $map_ptr_base,
727
                    $memory_locations,
728
                    $dereferencer,
729
                    $zend_type_reader,
730
                    $context_pools,
731
                );
732
                $call_frame_context->add('extra_named_params', $extra_named_params_context);
733
            }
734
        }
735
        return $call_frames_context;
736
    }
737
738
    public function collectGlobalVariables(
739
        ZendArray $array,
740
        int $map_ptr_base,
741
        Dereferencer $dereferencer,
742
        ZendTypeReader $zend_type_reader,
743
        MemoryLocations $memory_locations,
744
        ContextPools $context_pools
745
    ): GlobalVariablesContext {
746
        return GlobalVariablesContext::fromArrayContext(
747
            $this->collectZendArray(
748
                $array,
749
                $map_ptr_base,
750
                $dereferencer,
751
                $zend_type_reader,
752
                $memory_locations,
753
                $context_pools,
754
            )
755
        );
756
    }
757
758
    public function collectZendArray(
759
        ZendArray $array,
760
        int $map_ptr_base,
761
        Dereferencer $dereferencer,
762
        ZendTypeReader $zend_type_reader,
763
        MemoryLocations $memory_locations,
764
        ContextPools $context_pools
765
    ): ArrayHeaderContext {
766
        $array_header_location = ZendArrayMemoryLocation::fromZendArray($array);
767
        $array_table_location = ZendArrayTableMemoryLocation::fromZendArray($array);
768
        $array_table_overhead_location = ZendArrayTableOverheadMemoryLocation::fromZendArrayAndUsedLocation(
769
            $array,
770
            $array_table_location
771
        );
772
773
        $memory_locations->add($array_header_location);
774
        $memory_locations->add($array_table_location);
775
        $memory_locations->add($array_table_overhead_location);
776
777
        $array_header_context = $context_pools
778
            ->array_context_pool
779
            ->getContextForLocation($array_header_location)
780
        ;
781
        $array_context = new ArrayElementsContext($array_table_location);
782
        $overhead_context = new ArrayPossibleOverheadContext($array_table_overhead_location);
783
784
        foreach ($array->getItemIteratorWithZendStringKeyIfAssoc($dereferencer) as $key => $zval) {
785
            $element_context = new ArrayElementContext();
786
            if ($key instanceof Pointer) {
787
                $key_context = $this->collectZendStringPointer(
788
                    $key,
789
                    $memory_locations,
790
                    $dereferencer,
791
                    $context_pools,
792
                );
793
                $zend_string = $dereferencer->deref($key);
794
                $key_string = $zend_string->toString($dereferencer);
795
                $element_context->add('key', $key_context);
796
            } else {
797
                $key_string = (string)$key;
798
            }
799
            $array_context->add($key_string, $element_context);
800
            $value_context = $this->collectZval(
801
                $zval,
802
                $map_ptr_base,
803
                $dereferencer,
804
                $zend_type_reader,
805
                $memory_locations,
806
                $context_pools,
807
            );
808
            if (!is_null($value_context)) {
809
                $element_context->add('value', $value_context);
810
            }
811
        }
812
        $array_header_context->add('possible_unused_area', $overhead_context);
813
        $array_header_context->add('array_elements', $array_context);
814
        return $array_header_context;
815
    }
816
817
    public function collectZendObject(
818
        ZendObject $object,
819
        int $map_ptr_base,
820
        Dereferencer $dereferencer,
821
        ZendTypeReader $zend_type_reader,
822
        MemoryLocations $memory_locations,
823
        ContextPools $context_pools
824
    ): ObjectContext {
825
        $object_location = ZendObjectMemoryLocation::fromZendObject(
826
            $object,
827
            $dereferencer,
828
            $zend_type_reader,
829
        );
830
        $object_handlers_memory_location = ZendObjectHandlersMemoryLocation::fromZendObject(
831
            $object,
832
            $zend_type_reader,
833
        );
834
        $memory_locations->add($object_location);
835
        $memory_locations->add($object_handlers_memory_location);
836
837
        $object_context = $context_pools
838
            ->object_context_pool->getContextForLocation(
839
                $object_location,
840
            )
841
        ;
842
        $object_handlers_context = $context_pools
843
            ->object_context_pool
844
            ->getHandlersContextForLocation(
845
                $object_handlers_memory_location,
846
            )
847
        ;
848
        $object_context->add('object_handlers', $object_handlers_context);
849
850
        if (
851
            !is_null($object->properties)
852
            and !is_null($object->ce)
853
            and !$object->isEnum($dereferencer)
854
        ) {
855
            $dynamic_properties_context = $this->collectZendArray(
856
                $dereferencer->deref($object->properties),
857
                $map_ptr_base,
858
                $dereferencer,
859
                $zend_type_reader,
860
                $memory_locations,
861
                $context_pools,
862
            );
863
            $object_context->add('dynamic_properties', $dynamic_properties_context);
864
        }
865
        $properties_iterator = $object->getPropertiesIterator(
866
            $dereferencer,
867
            $zend_type_reader,
868
        );
869
        foreach ($properties_iterator as $name => $property) {
870
            assert(is_string($name));
871
            $property_context = $this->collectZval(
872
                $property,
873
                $map_ptr_base,
874
                $dereferencer,
875
                $zend_type_reader,
876
                $memory_locations,
877
                $context_pools,
878
            );
879
            if (!is_null($property_context)) {
880
                $object_context->add($name, $property_context);
881
            }
882
        }
883
884
        assert(!is_null($object->ce));
885
        $class_entry = $dereferencer->deref($object->ce);
886
        if ($class_entry->getClassName($dereferencer) === 'Closure') {
887
            $closure_context = $this->collectClosure(
888
                $dereferencer->deref(
889
                    ZendClosure::getPointerFromZendObjectPointer(
890
                        $object->getPointer(),
891
                        $zend_type_reader,
892
                    ),
893
                ),
894
                $map_ptr_base,
895
                $dereferencer,
896
                $zend_type_reader,
897
                $memory_locations,
898
                $context_pools,
899
            );
900
            $object_context->add('closure', $closure_context);
901
        }
902
903
        return $object_context;
904
    }
905
906
    public function collectClosure(
907
        ZendClosure $zend_closure,
908
        int $map_ptr_base,
909
        Dereferencer $dereferencer,
910
        ZendTypeReader $zend_type_reader,
911
        MemoryLocations $memory_locations,
912
        ContextPools $context_pools,
913
    ): ClosureContext {
914
        $closure_context = new ClosureContext();
915
        $closure_context->add(
916
            'func',
917
            $this->collectZendFunctionPointer(
918
                $zend_closure->func->getPointer(),
919
                $map_ptr_base,
920
                $dereferencer,
921
                $zend_type_reader,
922
                $memory_locations,
923
                $context_pools,
924
            )
925
        );
926
        $zval_context = $this->collectZval(
927
            $zend_closure->this_ptr,
928
            $map_ptr_base,
929
            $dereferencer,
930
            $zend_type_reader,
931
            $memory_locations,
932
            $context_pools,
933
        );
934
        if (!is_null($zval_context)) {
935
            $closure_context->add(
936
                'this_ptr',
937
                $zval_context,
938
            );
939
        }
940
        return $closure_context;
941
    }
942
943
    public function collectFunctionTable(
944
        ZendArray $array,
945
        int $map_ptr_base,
946
        Dereferencer $dereferencer,
947
        ZendTypeReader $zend_type_reader,
948
        MemoryLocations $memory_locations,
949
        ContextPools $context_pools
950
    ): DefinedFunctionsContext {
951
        $array_header_location = ZendArrayMemoryLocation::fromZendArray($array);
952
        $array_table_location = ZendArrayTableMemoryLocation::fromZendArray($array);
953
        $array_table_overhead_location = ZendArrayTableOverheadMemoryLocation::fromZendArrayAndUsedLocation(
954
            $array,
955
            $array_table_location
956
        );
957
958
        $memory_locations->add($array_header_location);
959
        $memory_locations->add($array_table_location);
960
        $memory_locations->add($array_table_overhead_location);
961
962
        $defined_functions_context = new DefinedFunctionsContext(
963
            $array_header_location,
964
            $array_table_location,
965
        );
966
967
        foreach ($array->getItemIterator($dereferencer) as $function_name => $zval) {
968
            assert(is_string($function_name));
969
            assert(!is_null($zval->value->func));
970
            $function_context = $this->collectZendFunctionPointer(
971
                $zval->value->func,
972
                $map_ptr_base,
973
                $dereferencer,
974
                $zend_type_reader,
975
                $memory_locations,
976
                $context_pools,
977
            );
978
            $defined_functions_context->add($function_name, $function_context);
979
        }
980
        return $defined_functions_context;
981
    }
982
983
    /** @param Pointer<ZendFunction> $pointer */
984
    public function collectZendFunctionPointer(
985
        Pointer $pointer,
986
        int $map_ptr_base,
987
        Dereferencer $dereferencer,
988
        ZendTypeReader $zend_type_reader,
989
        MemoryLocations $memory_locations,
990
        ContextPools $context_pools
991
    ): FunctionDefinitionContext {
992
        if ($memory_locations->has($pointer->address)) {
993
            $memory_location = $memory_locations->get($pointer->address);
994
            if ($memory_location instanceof ZendOpArrayHeaderMemoryLocation) {
995
                return $context_pools
996
                    ->user_function_definition_context_pool
997
                    ->getContextForLocation($memory_location)
998
                ;
999
            }
1000
        }
1001
        $func = $dereferencer->deref($pointer);
1002
        return $this->collectZendFunction(
1003
            $func,
1004
            $map_ptr_base,
1005
            $dereferencer,
1006
            $zend_type_reader,
1007
            $memory_locations,
1008
            $context_pools,
1009
        );
1010
    }
1011
1012
    public function collectZendFunction(
1013
        ZendFunction $func,
1014
        int $map_ptr_base,
1015
        Dereferencer $dereferencer,
1016
        ZendTypeReader $zend_type_reader,
1017
        MemoryLocations $memory_locations,
1018
        ContextPools $context_pools
1019
    ): FunctionDefinitionContext {
1020
        if ($func->isUserFunction()) {
1021
            $function_definition_context = $this->collectUserFunctionDefinition(
1022
                $func,
1023
                $map_ptr_base,
1024
                $dereferencer,
1025
                $zend_type_reader,
1026
                $memory_locations,
1027
                $context_pools,
1028
            );
1029
        } else {
1030
            $function_definition_context = new InternalFunctionDefinitionContext();
1031
        }
1032
        if (!is_null($func->function_name)) {
1033
            $function_name_context = $this->collectZendStringPointer(
1034
                $func->function_name,
1035
                $memory_locations,
1036
                $dereferencer,
1037
                $context_pools,
1038
            );
1039
            $function_definition_context->add('name', $function_name_context);
1040
        }
1041
        return $function_definition_context;
1042
    }
1043
1044
    public function collectUserFunctionDefinition(
1045
        ZendFunction $func,
1046
        int $map_ptr_base,
1047
        Dereferencer $dereferencer,
1048
        ZendTypeReader $zend_type_reader,
1049
        MemoryLocations $memory_locations,
1050
        ContextPools $context_pools
1051
    ): UserFunctionDefinitionContext {
1052
        $function_name = $func->getFullyQualifiedFunctionName(
1053
            $dereferencer
1054
        );
1055
        $op_array_header_memory_location = ZendOpArrayHeaderMemoryLocation::fromZendFunction(
1056
            $func,
1057
            $zend_type_reader,
1058
            $function_name
1059
        );
1060
        $op_array_body_memory_location = ZendOpArrayBodyMemoryLocation::fromZendFunction(
1061
            $func,
1062
            $zend_type_reader,
1063
            $function_name
1064
        );
1065
        $memory_locations->add($op_array_header_memory_location);
1066
        $memory_locations->add($op_array_body_memory_location);
1067
        $function_definition_context = $context_pools
1068
            ->user_function_definition_context_pool
1069
            ->getContextForLocation($op_array_header_memory_location)
1070
        ;
1071
        $op_array_context = new OpArrayContext(
1072
            $op_array_header_memory_location,
1073
            $op_array_body_memory_location,
1074
        );
1075
        $function_definition_context->add('op_array', $op_array_context);
1076
1077
        if ($func->op_array->cache_size > 0) {
1078
            $runtime_cache_memory_location = RuntimeCacheMemoryLocation::fromZendOpArray(
1079
                $func->op_array,
1080
                $dereferencer,
1081
                $zend_type_reader,
1082
                $map_ptr_base,
1083
            );
1084
            if ($runtime_cache_memory_location->address !== 0) {
1085
                $memory_locations->add($runtime_cache_memory_location);
1086
                $run_time_cache_context = new RuntimeCacheContext($runtime_cache_memory_location);
1087
                $op_array_context->add('run_time_cache', $run_time_cache_context);
1088
            }
1089
        }
1090
1091
        if (!is_null($func->op_array->arg_info)) {
1092
            $arginfos_memory_location = ZendArgInfosMemoryLocation::fromZendOpArray(
1093
                $func->op_array,
1094
                $zend_type_reader,
1095
            );
1096
            $memory_locations->add($arginfos_memory_location);
1097
            $arginfos_context = new ArgInfosContext($arginfos_memory_location);
1098
            $op_array_context->add('arg_infos', $arginfos_context);
1099
            foreach ($func->op_array->iterateArgInfo($dereferencer, $zend_type_reader) as $arg_info) {
1100
                if (!is_null($arg_info->name)) {
1101
                    $arg_info_context = new ArgInfoContext();
1102
                    $arg_info_name_context = $this->collectZendStringPointer(
1103
                        $arg_info->name,
1104
                        $memory_locations,
1105
                        $dereferencer,
1106
                        $context_pools,
1107
                    );
1108
                    $arg_info_name = $dereferencer
1109
                        ->deref($arg_info->name)
1110
                        ->toString($dereferencer)
1111
                    ;
1112
                    $arg_info_context->add('name', $arg_info_name_context);
1113
                    $arginfos_context->add($arg_info_name, $arg_info_context);
1114
                }
1115
            }
1116
        }
1117
1118
        if (!is_null($func->op_array->doc_comment)) {
1119
            $doc_comment_context = $this->collectZendStringPointer(
1120
                $func->op_array->doc_comment,
1121
                $memory_locations,
1122
                $dereferencer,
1123
                $context_pools,
1124
            );
1125
            $op_array_context->add('doc_comment', $doc_comment_context);
1126
        }
1127
1128
        if (!is_null($func->op_array->filename)) {
1129
            $file_name_context = $this->collectZendStringPointer(
1130
                $func->op_array->filename,
1131
                $memory_locations,
1132
                $dereferencer,
1133
                $context_pools,
1134
            );
1135
            $op_array_context->add('filename', $file_name_context);
1136
        }
1137
1138
        if (!is_null($func->op_array->static_variables)) {
1139
            $static_variables_context = $this->collectZendArray(
1140
                $dereferencer->deref($func->op_array->static_variables),
1141
                $map_ptr_base,
1142
                $dereferencer,
1143
                $zend_type_reader,
1144
                $memory_locations,
1145
                $context_pools,
1146
            );
1147
            $op_array_context->add('static_variables', $static_variables_context);
1148
        }
1149
1150
        if (!is_null($func->op_array->vars)) {
1151
            $local_variable_name_table_location = LocalVariableNameTableMemoryLocation::fromZendOpArray(
1152
                $func->op_array
1153
            );
1154
            $memory_locations->add($local_variable_name_table_location);
1155
            $variable_name_table_context = new LocalVariableNameTableContext(
1156
                $local_variable_name_table_location
1157
            );
1158
            $op_array_context->add('variable_name_table', $variable_name_table_context);
1159
1160
            $variable_names_iterator = $func->op_array->getVariableNamesAsIteratorOfPointersToZendStrings(
1161
                $dereferencer,
1162
                $zend_type_reader,
1163
            );
1164
            foreach ($variable_names_iterator as $key => $variable_name) {
1165
                $variable_name_context = $this->collectZendStringPointer(
1166
                    $variable_name,
1167
                    $memory_locations,
1168
                    $dereferencer,
1169
                    $context_pools,
1170
                );
1171
                $variable_name_table_context->add((string)$key, $variable_name_context);
1172
            }
1173
        }
1174
1175
        if ($func->op_array->num_dynamic_func_defs > 0) {
1176
            $dynamic_func_defs_table_memory_location = DynamicFuncDefsTableMemoryLocation::fromZendOpArray(
1177
                $func->op_array,
1178
            );
1179
            $memory_locations->add($dynamic_func_defs_table_memory_location);
1180
            $dynamic_func_defs_context = new DynamicFuncDefsContext(
1181
                $dynamic_func_defs_table_memory_location
1182
            );
1183
            $dynamic_func_defs_iterator = $func->op_array->iterateDynamicFunctionDefinitions(
1184
                $dereferencer,
1185
                $zend_type_reader,
1186
            );
1187
            foreach ($dynamic_func_defs_iterator as $key => $dynamic_func_def) {
1188
                $dynamic_function_context = $this->collectZendFunctionPointer(
1189
                    $dynamic_func_def,
1190
                    $map_ptr_base,
1191
                    $dereferencer,
1192
                    $zend_type_reader,
1193
                    $memory_locations,
1194
                    $context_pools,
1195
                );
1196
                $dynamic_func_defs_context->add((string)$key, $dynamic_function_context);
1197
            }
1198
            $op_array_context->add('dynamic_function_definitions', $dynamic_func_defs_context);
1199
        }
1200
        return $function_definition_context;
1201
    }
1202
1203
    public function collectClassConstantsTable(
1204
        ZendArray $array,
1205
        int $map_ptr_base,
1206
        Dereferencer $dereferencer,
1207
        ZendTypeReader $zend_type_reader,
1208
        MemoryLocations $memory_locations,
1209
        ContextPools $context_pools
1210
    ): ClassConstantsContext {
1211
        $array_table_location = ZendArrayTableMemoryLocation::fromZendArray($array);
1212
        $array_table_overhead_location = ZendArrayTableOverheadMemoryLocation::fromZendArrayAndUsedLocation(
1213
            $array,
1214
            $array_table_location
1215
        );
1216
        $memory_locations->add($array_table_location);
1217
        $memory_locations->add($array_table_overhead_location);
1218
        $class_constants_context = new ClassConstantsContext($array_table_location);
1219
1220
        $array_iterator = $array->getItemIteratorWithZendStringKeyIfAssoc($dereferencer);
1221
        foreach ($array_iterator as $constant_name => $zval_or_ptr) {
1222
            assert($constant_name instanceof Pointer);
1223
            $constant_name_context = $this->collectZendStringPointer(
1224
                $constant_name,
1225
                $memory_locations,
1226
                $dereferencer,
1227
                $context_pools,
1228
            );
1229
            $zend_string = $dereferencer->deref($constant_name);
1230
            $constant_name_string = $zend_string->toString($dereferencer);
1231
            $constant_context = new ClassConstantContext();
1232
            $class_constants_context->add($constant_name_string, $constant_context);
1233
            $constant_context->add('name', $constant_name_context);
1234
1235
            if ($zend_type_reader->isPhpVersionLowerThan(ZendTypeReader::V71)) {
1236
                $zval = $zval_or_ptr;
1237
            } else {
1238
                $class_constant_ptr = $zval_or_ptr->value->getAsPointer(
1239
                    ZendClassConstant::class,
1240
                    $zend_type_reader->sizeOf(ZendClassConstant::getCTypeName()),
1241
                );
1242
                $class_constant = $dereferencer->deref(
1243
                    $class_constant_ptr
1244
                );
1245
                $memory_location = ZendClassConstantMemoryLocation::fromZendClassConstant(
1246
                    $class_constant,
1247
                );
1248
                $memory_locations->add($memory_location);
1249
                $zval = $class_constant->value;
1250
                $info_context = new ClassConstantInfoContext($memory_location);
1251
                $constant_context->add('info', $info_context);
1252
            }
1253
            $value_context = $this->collectZval(
1254
                $zval,
1255
                $map_ptr_base,
1256
                $dereferencer,
1257
                $zend_type_reader,
1258
                $memory_locations,
1259
                $context_pools,
1260
            );
1261
            if (!is_null($value_context)) {
1262
                $constant_context->add('value', $value_context);
1263
            }
1264
        }
1265
1266
        return $class_constants_context;
1267
    }
1268
1269
    public function collectClassTable(
1270
        ZendArray $array,
1271
        int $map_ptr_base,
1272
        Dereferencer $dereferencer,
1273
        ZendTypeReader $zend_type_reader,
1274
        MemoryLocations $memory_locations,
1275
        ContextPools $context_pools
1276
    ): DefinedClassesContext {
1277
        $defined_classes_context = new DefinedClassesContext();
1278
        foreach ($array->getItemIterator($dereferencer) as $class_name => $zval) {
1279
            assert(!is_null($zval->value->ce));
1280
            $class_definition_context = $this->collectClassDefinitionPointer(
1281
                $zval->value->ce,
1282
                $map_ptr_base,
1283
                $dereferencer,
1284
                $zend_type_reader,
1285
                $memory_locations,
1286
                $context_pools,
1287
            );
1288
            if (!is_null($class_definition_context)) {
1289
                $defined_classes_context->add((string)$class_name, $class_definition_context);
1290
            }
1291
        }
1292
        return $defined_classes_context;
1293
    }
1294
1295
    /** @param Pointer<ZendClassEntry> $pointer */
1296
    private function collectClassDefinitionPointer(
1297
        Pointer $pointer,
1298
        int $map_ptr_base,
1299
        Dereferencer $dereferencer,
1300
        ZendTypeReader $zend_type_reader,
1301
        MemoryLocations $memory_locations,
1302
        ContextPools $context_pools,
1303
    ): ?ClassDefinitionContext {
1304
        if ($memory_locations->has($pointer->address)) {
1305
            return null;
1306
        }
1307
        $class_entry = $dereferencer->deref($pointer);
1308
        return $this->collectClassDefinition(
1309
            $class_entry,
1310
            $map_ptr_base,
1311
            $dereferencer,
1312
            $zend_type_reader,
1313
            $memory_locations,
1314
            $context_pools,
1315
        );
1316
    }
1317
1318
    private function collectClassDefinition(
1319
        ZendClassEntry $class_entry,
1320
        int $map_ptr_base,
1321
        Dereferencer $dereferencer,
1322
        ZendTypeReader $zend_type_reader,
1323
        MemoryLocations $memory_locations,
1324
        ContextPools $context_pools,
1325
    ): ClassDefinitionContext {
1326
        $class_definition_context = new ClassDefinitionContext($class_entry->isInternal());
1327
        $memory_location = ZendClassEntryMemoryLocation::fromZendClassEntry($class_entry);
1328
        $memory_locations->add($memory_location);
1329
        $class_entry_context = new ClassEntryContext($memory_location);
1330
        $class_definition_context->add('class_entry', $class_entry_context);
1331
1332
        $class_name_context = $this->collectZendStringPointer(
1333
            $class_entry->name,
1334
            $memory_locations,
1335
            $dereferencer,
1336
            $context_pools,
1337
        );
1338
        $class_definition_context->add('name', $class_name_context);
1339
1340
        if (!$class_entry->isInternal()) {
1341
            if (!is_null($class_entry->info->user->filename)) {
1342
                $file_name_context = $this->collectZendStringPointer(
1343
                    $class_entry->info->user->filename,
1344
                    $memory_locations,
1345
                    $dereferencer,
1346
                    $context_pools,
1347
                );
1348
                $class_definition_context->add('filename', $file_name_context);
1349
            }
1350
1351
            if (!is_null($class_entry->info->user->doc_comment)) {
1352
                $doc_comment_context = $this->collectZendStringPointer(
1353
                    $class_entry->info->user->doc_comment,
1354
                    $memory_locations,
1355
                    $dereferencer,
1356
                    $context_pools,
1357
                );
1358
                $class_definition_context->add('doc_comment', $doc_comment_context);
1359
            }
1360
        }
1361
1362
        if (
1363
            $class_entry->default_static_members_count > 0
1364
            and !is_null($class_entry->static_members_table)
1365
        ) {
1366
            $static_members_table_memory_location = StaticMembersTableMemoryLocation::fromZendClassEntry(
1367
                $class_entry,
1368
                $zend_type_reader,
1369
                $dereferencer,
1370
                $map_ptr_base,
1371
            );
1372
            $memory_locations->add($static_members_table_memory_location);
1373
            $static_properties_context = new ClassStaticPropertiesContext(
1374
                $static_members_table_memory_location
1375
            );
1376
            $static_property_iterator = $class_entry->getStaticPropertyIterator(
1377
                $dereferencer,
1378
                $zend_type_reader,
1379
                $map_ptr_base,
1380
            );
1381
            foreach ($static_property_iterator as $name => $value) {
1382
                $static_property_context = $this->collectZval(
1383
                    $value,
1384
                    $map_ptr_base,
1385
                    $dereferencer,
1386
                    $zend_type_reader,
1387
                    $memory_locations,
1388
                    $context_pools,
1389
                );
1390
                if (!is_null($static_property_context)) {
1391
                    $static_properties_context->add($name, $static_property_context);
1392
                }
1393
            }
1394
            $class_definition_context->add('static_properties', $static_properties_context);
1395
        }
1396
1397
        $properties_info_context = $this->collectPropertiesInfo(
1398
            $class_entry,
1399
            $dereferencer,
1400
            $zend_type_reader,
1401
            $memory_locations,
1402
            $context_pools,
1403
        );
1404
        $class_definition_context->add('property_info', $properties_info_context);
1405
1406
        if (!is_null($class_entry->default_properties_table)) {
1407
            $default_properties_table_memory_location = DefaultPropertiesTableMemoryLocation::fromZendClassEntry(
1408
                $class_entry,
1409
            );
1410
            $memory_locations->add($default_properties_table_memory_location);
1411
            $default_properties_context = new DefaultPropertiesTableContext(
1412
                $default_properties_table_memory_location
1413
            );
1414
            $class_definition_context->add('default_properties', $default_properties_context);
1415
        }
1416
1417
        if (!is_null($class_entry->default_static_members_table)) {
1418
            $default_static_members_memory_location = DefaultStaticMembersTableMemoryLocation::fromZendClassEntry(
1419
                $class_entry,
1420
            );
1421
            $memory_locations->add($default_static_members_memory_location);
1422
            $default_static_properties_context = new DefaultStaticPropertiesContext(
1423
                $default_static_members_memory_location
1424
            );
1425
            $class_definition_context->add('default_static_properties', $default_static_properties_context);
1426
        }
1427
1428
        $methods_context = $this->collectFunctionTable(
1429
            $class_entry->function_table,
1430
            $map_ptr_base,
1431
            $dereferencer,
1432
            $zend_type_reader,
1433
            $memory_locations,
1434
            $context_pools,
1435
        );
1436
        $class_definition_context->add('methods', $methods_context);
1437
1438
        $class_constants_context = $this->collectClassConstantsTable(
1439
            $class_entry->constants_table,
1440
            $map_ptr_base,
1441
            $dereferencer,
1442
            $zend_type_reader,
1443
            $memory_locations,
1444
            $context_pools,
1445
        );
1446
        $class_definition_context->add('constants', $class_constants_context);
1447
1448
        return $class_definition_context;
1449
    }
1450
1451
    private function collectPropertiesInfo(
1452
        ZendClassEntry $class_entry,
1453
        Dereferencer $dereferencer,
1454
        ZendTypeReader $zend_type_reader,
1455
        MemoryLocations $memory_locations,
1456
        ContextPools $context_pools
1457
    ): PropertiesInfoContext {
1458
        $memory_location = ZendArrayTableMemoryLocation::fromZendArray($class_entry->properties_info);
1459
        $memory_locations->add($memory_location);
1460
1461
        $properties_info_context = new PropertiesInfoContext($memory_location);
1462
1463
        foreach ($class_entry->iteratePropertyInfo($dereferencer, $zend_type_reader) as $key => $property_info) {
1464
            $property_info_memory_location = ZendPropertyInfoMemoryLocation::fromZendPropertyInfo(
1465
                $property_info,
1466
            );
1467
            $memory_locations->add($property_info_memory_location);
1468
            $property_info_context = new PropertyInfoContext($property_info_memory_location);
1469
            if (!is_null($property_info->name)) {
1470
                $property_info_name_context = $this->collectZendStringPointer(
1471
                    $property_info->name,
1472
                    $memory_locations,
1473
                    $dereferencer,
1474
                    $context_pools,
1475
                );
1476
                $property_info_context->add('name', $property_info_name_context);
1477
            }
1478
            if (!is_null($property_info->doc_comment)) {
1479
                $property_info_doc_comment_context = $this->collectZendStringPointer(
1480
                    $property_info->doc_comment,
1481
                    $memory_locations,
1482
                    $dereferencer,
1483
                    $context_pools,
1484
                );
1485
                $property_info_context->add('doc_comment', $property_info_doc_comment_context);
1486
            }
1487
            $properties_info_context->add($key, $property_info_context);
1488
        }
1489
        return $properties_info_context;
1490
    }
1491
1492
    private function collectGlobalConstants(
1493
        ZendArray $array,
1494
        int $map_ptr_base,
1495
        Dereferencer $dereferencer,
1496
        ZendTypeReader $zend_type_reader,
1497
        MemoryLocations $memory_locations,
1498
        ContextPools $context_pools
1499
    ): GlobalConstantsContext {
1500
        $memory_location = ZendArrayTableMemoryLocation::fromZendArray($array);
1501
        $memory_locations->add($memory_location);
1502
1503
        $global_constants_context = new GlobalConstantsContext($memory_location);
1504
        foreach ($array->getItemIterator($dereferencer) as $constant_name => $zval) {
1505
            assert(is_string($constant_name));
1506
            $zend_constant = $dereferencer->deref(
1507
                $zval->value->getAsPointer(
1508
                    ZendConstant::class,
1509
                    $zend_type_reader->sizeOf(ZendConstant::getCTypeName()),
1510
                )
1511
            );
1512
            $zend_constant_memory_location = ZendConstantMemoryLocation::fromZendConstant(
1513
                $zend_constant
1514
            );
1515
            $constant_context = new GlobalConstantContext($zend_constant_memory_location);
1516
            $global_constants_context->add($constant_name, $constant_context);
1517
1518
            if (!is_null($zend_constant->name)) {
1519
                $constant_name_context = $this->collectZendStringPointer(
1520
                    $zend_constant->name,
1521
                    $memory_locations,
1522
                    $dereferencer,
1523
                    $context_pools,
1524
                );
1525
                $constant_context->add('name', $constant_name_context);
1526
            }
1527
1528
            $value_context = $this->collectZval(
1529
                $zend_constant->value,
1530
                $map_ptr_base,
1531
                $dereferencer,
1532
                $zend_type_reader,
1533
                $memory_locations,
1534
                $context_pools,
1535
            );
1536
            if (!is_null($value_context)) {
1537
                $constant_context->add('value', $value_context);
1538
            }
1539
        }
1540
        return $global_constants_context;
1541
    }
1542
1543
    private function collectIncludedFiles(
1544
        ZendArray $included_files,
1545
        Dereferencer $dereferencer,
1546
        MemoryLocations $memory_locations,
1547
        ContextPools $context_pools
1548
    ): IncludedFilesContext {
1549
        $array_table_location = ZendArrayTableMemoryLocation::fromZendArray($included_files);
1550
        $included_files_context = new IncludedFilesContext($array_table_location);
1551
1552
        $memory_locations->add($array_table_location);
1553
1554
        $iterator = $included_files->getItemIteratorWithZendStringKeyIfAssoc($dereferencer);
1555
        foreach ($iterator as $filename => $_) {
1556
            assert($filename instanceof Pointer);
1557
            $raw_string = $dereferencer->deref($filename)->toString($dereferencer);
1558
            $included_file_context = $this->collectZendStringPointer(
1559
                $filename,
1560
                $memory_locations,
1561
                $dereferencer,
1562
                $context_pools,
1563
            );
1564
            $included_files_context->add($raw_string, $included_file_context);
1565
        }
1566
        return $included_files_context;
1567
    }
1568
1569
    private function collectInternedStrings(
1570
        ZendArray $interned_string,
1571
        int $map_ptr_base,
1572
        Dereferencer $dereferencer,
1573
        ZendTypeReader $zend_type_reader,
1574
        MemoryLocations $memory_locations,
1575
        ContextPools $context_pools
1576
    ): ArrayHeaderContext {
1577
        return $this->collectZendArray(
1578
            $interned_string,
1579
            $map_ptr_base,
1580
            $dereferencer,
1581
            $zend_type_reader,
1582
            $memory_locations,
1583
            $context_pools,
1584
        );
1585
    }
1586
1587
    private function collectObjectsStore(
1588
        ZendObjectsStore $objects_store,
1589
        int $map_ptr_base,
1590
        Dereferencer $dereferencer,
1591
        ZendTypeReader $zend_type_reader,
1592
        MemoryLocations $memory_locations,
1593
        ContextPools $context_pools
1594
    ): ObjectsStoreContext {
1595
        $objects_store_memory_location = ObjectsStoreMemoryLocation::fromZendObjectsStore(
1596
            $objects_store,
1597
        );
1598
        $objects_store_context = new ObjectsStoreContext($objects_store_memory_location);
1599
        $memory_locations->add($objects_store_memory_location);
1600
1601
        assert($objects_store->object_buckets instanceof Pointer);
1602
        $buckets = $dereferencer->deref($objects_store->object_buckets);
1603
        $bucket_iterator = $buckets->getIteratorOfPointersTo(
1604
            ZendObject::class,
1605
            $zend_type_reader,
1606
        );
1607
1608
        foreach ($bucket_iterator as $key => $bucket) {
1609
            if ($key === 0) {
1610
                continue;
1611
            }
1612
            if ($bucket->address & 1) {
1613
                continue;
1614
            }
1615
            if ($bucket->address === 0) {
1616
                continue;
1617
            }
1618
            $objects_store_bucket_context = $this->collectZendObjectPointer(
1619
                $bucket,
1620
                $map_ptr_base,
1621
                $memory_locations,
1622
                $dereferencer,
1623
                $zend_type_reader,
1624
                $context_pools,
1625
            );
1626
            $objects_store_context->add((string)$key, $objects_store_bucket_context);
1627
        }
1628
        return $objects_store_context;
1629
    }
1630
}
1631