ZendOpArray   A
last analyzed

Complexity

Total Complexity 39

Size/Duplication

Total Lines 337
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 162
c 1
b 0
f 0
dl 0
loc 337
rs 9.28
wmc 39

16 Methods

Rating   Name   Duplication   Size   Complexity  
A getLiveRange() 0 12 3
A getLastLiveRange() 0 6 2
B __get() 0 75 9
A getOpNumFromOpline() 0 4 1
A __construct() 0 24 1
A getRuntimeCacheAddress() 0 7 2
A getFileName() 0 7 2
A findLiveTmpVars() 0 16 4
A isClosure() 0 3 1
A getDisplayNameForClosure() 0 5 1
A getVariableNamesAsIteratorOfPointersToZendStrings() 0 11 2
A getVariableNames() 0 10 2
A hasReturnType() 0 3 1
A getNumDynamicFuncDefs() 0 7 2
A iterateDynamicFunctionDefinitions() 0 11 2
A iterateArgInfo() 0 13 4
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\PhpInternals\Types\Zend;
15
16
use FFI;
17
use FFI\CData;
18
use FFI\PhpInternals\zend_op_array;
19
use PhpCast\Cast;
0 ignored issues
show
Bug introduced by
The type PhpCast\Cast 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...
20
use Reli\Lib\PhpInternals\Types\C\PointerArray;
21
use Reli\Lib\PhpInternals\ZendTypeReader;
22
use Reli\Lib\Process\Pointer\Dereferencer;
23
use Reli\Lib\Process\Pointer\Pointer;
24
25
class ZendOpArray
26
{
27
    /**
28
     * @var Pointer<ZendString>|null
29
     * @psalm-suppress PropertyNotSetInConstructor
30
     */
31
    public ?Pointer $filename;
32
33
    /**
34
     * @var Pointer<ZendArgInfo>|null
35
     * @psalm-suppress PropertyNotSetInConstructor
36
     */
37
    public ?Pointer $arg_info;
38
39
    /**
40
     * @psalm-suppress PropertyNotSetInConstructor
41
     * @var Pointer<ZendString>|null
42
     */
43
    public ?Pointer $doc_comment;
44
45
    /**
46
     * @var Pointer<ZendArray>|null
47
     * @psalm-suppress PropertyNotSetInConstructor
48
     */
49
    public ?Pointer $static_variables;
50
51
    /** @psalm-suppress PropertyNotSetInConstructor */
52
    public int $fn_flags;
53
54
    /** @psalm-suppress PropertyNotSetInConstructor */
55
    public int $last;
56
57
    /** @psalm-suppress PropertyNotSetInConstructor */
58
    public int $T;
59
60
    /** @psalm-suppress PropertyNotSetInConstructor */
61
    public int $num_args;
62
63
    /** @psalm-suppress PropertyNotSetInConstructor */
64
    public int $last_var;
65
66
    /** @psalm-suppress PropertyNotSetInConstructor */
67
    public int $last_literal;
68
    /**
69
     * @var Pointer<ZendArray>|null
70
     * @psalm-suppress PropertyNotSetInConstructor
71
     */
72
    public ?Pointer $literals;
73
74
    /** @var Pointer<PointerArray>|null */
75
    public ?Pointer $vars;
76
77
    /** @var Pointer<ZendOp>|null */
78
    public ?Pointer $opcodes;
79
80
    /** @psalm-suppress PropertyNotSetInConstructor */
81
    public int $last_live_range;
82
    /**
83
     * @psalm-suppress PropertyNotSetInConstructor
84
     * @var Pointer<ZendLiveRange>|null
85
     */
86
    public ?Pointer $live_range;
87
88
    /** @psalm-suppress PropertyNotSetInConstructor */
89
    public int $num_dynamic_func_defs;
90
91
    /** @var Pointer<PointerArray>|null */
92
    public ?Pointer $dynamic_func_defs;
93
94
    /** @psalm-suppress PropertyNotSetInConstructor */
95
    public int $cache_size;
96
97
    /** @psalm-suppress PropertyNotSetInConstructor */
98
    public int $run_time_cache__ptr;
99
100
    /** @psalm-suppress PropertyNotSetInConstructor */
101
    public int $line_start;
102
103
    /** @psalm-suppress PropertyNotSetInConstructor */
104
    public int $line_end;
105
106
    /** @param zend_op_array $cdata */
107
    public function __construct(
108
        private CData $cdata,
109
    ) {
110
        unset($this->fn_flags);
111
        unset($this->filename);
112
        unset($this->arg_info);
113
        unset($this->static_variables);
114
        unset($this->last);
115
        unset($this->T);
116
        unset($this->num_args);
117
        unset($this->last_var);
118
        unset($this->vars);
119
        unset($this->opcodes);
120
        unset($this->last_live_range);
121
        unset($this->live_range);
122
        unset($this->doc_comment);
123
        unset($this->last_literal);
124
        unset($this->literals);
125
        unset($this->num_dynamic_func_defs);
126
        unset($this->dynamic_func_defs);
127
        unset($this->cache_size);
128
        unset($this->run_time_cache__ptr);
129
        unset($this->line_start);
130
        unset($this->line_end);
131
    }
132
133
    public function __get(string $field_name)
134
    {
135
        return match ($field_name) {
136
            'fn_flags' => $this->fn_flags = $this->cdata->fn_flags,
137
            'filename' => $this->filename = $this->cdata->filename !== null
138
                ? Pointer::fromCData(
139
                    ZendString::class,
140
                    $this->cdata->filename,
141
                )
142
                : null
143
            ,
144
            'arg_info' => $this->arg_info = $this->cdata->arg_info !== null
145
                ? Pointer::fromCData(
146
                    ZendArgInfo::class,
147
                    $this->cdata->arg_info,
148
                )
149
                : null
150
            ,
151
            'doc_comment' => $this->doc_comment
152
                = $this->cdata->doc_comment !== null
153
                ? Pointer::fromCData(
154
                    ZendString::class,
155
                    $this->cdata->doc_comment,
156
                )
157
                : null
158
            ,
159
            'static_variables' => $this->static_variables = $this->cdata->static_variables !== null
160
                ? Pointer::fromCData(
161
                    ZendArray::class,
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...
162
                    $this->cdata->static_variables,
163
                )
164
                : null
165
            ,
166
            'last' => $this->cdata->last,
167
            'T' => $this->cdata->T,
168
            'num_args' => $this->cdata->num_args,
169
            'last_var' => $this->cdata->last_var,
170
            'vars' => $this->vars = $this->cdata->vars !== null
171
                ? PointerArray::createPointerToArray(
172
                    Cast::toInt(FFI::cast('long', $this->cdata->vars)?->cdata),
173
                    $this->cdata->last_var,
174
                )
175
                : null
176
            ,
177
            'opcodes' => $this->opcodes = $this->cdata->opcodes !== null
178
                ? Pointer::fromCData(
179
                    ZendOp::class,
180
                    $this->cdata->opcodes,
181
                )
182
                : null
183
            ,
184
            'last_live_range' => $this->last_live_range = $this->getLastLiveRange(),
185
            'live_range' => $this->live_range = $this->getLiveRange(),
186
            'last_literal' => $this->last_literal = $this->cdata->last_literal,
187
            'literals' => $this->literals = $this->cdata->literals !== null
188
                ? Pointer::fromCData(
189
                    ZendArray::class,
190
                    $this->cdata->literals,
191
                )
192
                : null
193
            ,
194
            'num_dynamic_func_defs' => $this->num_dynamic_func_defs = $this->getNumDynamicFuncDefs(),
195
            'dynamic_func_defs' => $this->dynamic_func_defs = $this->cdata->dynamic_func_defs !== null
196
                ? PointerArray::createPointerToArray(
197
                    Cast::toInt(
198
                        FFI::cast('long', $this->cdata->dynamic_func_defs)?->cdata,
199
                    ),
200
                    $this->cdata->num_dynamic_func_defs,
201
                )
202
                : null
203
            ,
204
            'cache_size' => $this->cache_size = $this->cdata->cache_size,
205
            'run_time_cache' => $this->getRuntimeCacheAddress(),
206
            'line_start' => $this->line_start = $this->cdata->line_start,
207
            'line_end' => $this->line_end = $this->cdata->line_end,
208
        };
209
    }
210
211
    /** @return Pointer<ZendLiveRange>|null */
212
    private function getLiveRange(): ?Pointer
213
    {
214
        if (in_array('live_range', \FFI::typeof($this->cdata)->getStructFieldNames(), true)) {
0 ignored issues
show
Bug introduced by
The method getStructFieldNames() does not exist on FFI\CType. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

214
        if (in_array('live_range', \FFI::typeof($this->cdata)->/** @scrutinizer ignore-call */ getStructFieldNames(), true)) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
215
            return $this->cdata->live_range !== null
216
                ? Pointer::fromCData(
217
                    ZendLiveRange::class,
218
                    $this->cdata->live_range,
219
                )
220
                : null
221
            ;
222
        }
223
        return null;
224
    }
225
226
    private function getLastLiveRange(): int
227
    {
228
        if (in_array('last_live_range', \FFI::typeof($this->cdata)->getStructFieldNames(), true)) {
229
            return $this->cdata->last_live_range;
230
        }
231
        return 0;
232
    }
233
234
    /** @param Pointer<ZendOp> $opline */
235
    public function getOpNumFromOpline(Pointer $opline): int
236
    {
237
        assert($this->opcodes !== null);
238
        return Cast::toInt(($opline->address - $this->opcodes->address) / $opline->size);
239
    }
240
241
    public function findLiveTmpVars(
242
        int $op_num,
243
        Dereferencer $dereferencer,
244
    ): array {
245
        if ($this->live_range === null) {
246
            return [];
247
        }
248
        $result = [];
249
        for ($i = 0; $i < $this->last_live_range; $i++) {
250
            $live_range = $dereferencer->deref($this->live_range->indexedAt($i));
251
            if ($live_range->isInRange($op_num)) {
252
                $tmp_var_num = $live_range->getTmpVarNum();
253
                $result[] = $tmp_var_num;
254
            }
255
        }
256
        return $result;
257
    }
258
259
    public function getRuntimeCacheAddress(): int
260
    {
261
        $ctype = FFI::typeof($this->cdata);
262
        if (in_array('run_time_cache__ptr', $ctype->getStructFieldNames(), true)) {
263
            return Cast::toInt(FFI::cast('long', $this->cdata->run_time_cache__ptr)?->cdata);
264
        } else {
265
            return Cast::toInt(FFI::cast('long', $this->cdata->run_time_cache)?->cdata);
266
        }
267
    }
268
269
    /** @return iterable<ZendArgInfo> */
270
    public function iterateArgInfo(
271
        Dereferencer $dereferencer,
272
        ZendTypeReader $zend_type_reader,
273
    ): iterable {
274
        if (is_null($this->arg_info)) {
275
            return [];
276
        }
277
        if ($this->hasReturnType($zend_type_reader)) {
278
            yield $dereferencer->deref($this->arg_info->indexedAt(-1));
279
        }
280
281
        for ($i = 0; $i < $this->num_args; $i++) {
282
            yield $dereferencer->deref($this->arg_info->indexedAt($i));
283
        }
284
    }
285
286
    public function isClosure(ZendTypeReader $zend_type_reader): bool
287
    {
288
        return (bool)($this->fn_flags & (int)$zend_type_reader->constants::ZEND_ACC_CLOSURE);
289
    }
290
291
    public function getDisplayNameForClosure(
292
        Dereferencer $dereferencer,
293
    ): string {
294
        $file_name = $this->getFileName($dereferencer) ?? '<unknown>';
295
        return '{closure}(' . $file_name . ':' . $this->line_start . '-' . $this->line_end . ')';
296
    }
297
298
    public function hasReturnType(ZendTypeReader $zend_type_reader): bool
299
    {
300
        return (bool)($this->fn_flags & (int)$zend_type_reader->constants::ZEND_ACC_HAS_RETURN_TYPE);
301
    }
302
303
    private function getNumDynamicFuncDefs(): int
304
    {
305
        $ctype = FFI::typeof($this->cdata);
306
        if (in_array('num_dynamic_func_defs', $ctype->getStructFieldNames(), true)) {
307
            return $this->cdata->num_dynamic_func_defs;
308
        }
309
        return 0;
310
    }
311
312
    public function getFileName(Dereferencer $dereferencer): ?string
313
    {
314
        if (is_null($this->filename)) {
315
            return null;
316
        }
317
        $filename = $dereferencer->deref($this->filename);
318
        return $filename->toString($dereferencer);
319
    }
320
321
    /** @return iterable<int, Pointer<ZendString>> */
322
    public function getVariableNamesAsIteratorOfPointersToZendStrings(
323
        Dereferencer $dereferencer,
324
        ZendTypeReader $zend_type_reader,
325
    ): iterable {
326
        if (is_null($this->vars)) {
327
            return [];
328
        }
329
        $vars = $dereferencer->deref($this->vars);
330
        return $vars->getIteratorOfPointersTo(
331
            ZendString::class,
332
            $zend_type_reader,
333
        );
334
    }
335
336
    /** @return iterable<int, string> */
337
    public function getVariableNames(Dereferencer $dereferencer, ZendTypeReader $zend_type_reader): iterable
338
    {
339
        $iterator = $this->getVariableNamesAsIteratorOfPointersToZendStrings(
340
            $dereferencer,
341
            $zend_type_reader,
342
        );
343
        foreach ($iterator as $key => $name_pointer) {
344
            $zend_string = $dereferencer->deref($name_pointer);
345
            $string = $zend_string->toString($dereferencer);
346
            yield $key => $string;
347
        }
348
    }
349
350
    /** @return iterable<int, Pointer<ZendFunction>> */
351
    public function iterateDynamicFunctionDefinitions(
352
        Dereferencer $dereferencer,
353
        ZendTypeReader $zend_type_reader,
354
    ): iterable {
355
        if (is_null($this->dynamic_func_defs)) {
356
            return [];
357
        }
358
        $dynamic_func_defs = $dereferencer->deref($this->dynamic_func_defs);
359
        return $dynamic_func_defs->getIteratorOfPointersTo(
360
            ZendFunction::class,
361
            $zend_type_reader,
362
        );
363
    }
364
}
365