Anuj-Panthri commited on
Commit
d29be41
1 Parent(s): 32c8469

added tests for the TODOListController

Browse files
app/Http/Controllers/TODOListController.php CHANGED
@@ -33,6 +33,7 @@ public function changeStatus($id,Request $request){
33
 
34
  return redirect("/");
35
  }
 
36
  public function deleteItem(Request $request){
37
  // \Log::info(json_encode($request->all()));
38
  $listitem = ListItem::where("id",$request->id)->first();
 
33
 
34
  return redirect("/");
35
  }
36
+
37
  public function deleteItem(Request $request){
38
  // \Log::info(json_encode($request->all()));
39
  $listitem = ListItem::where("id",$request->id)->first();
database/factories/ListItemFactory.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Database\Factories;
4
+
5
+ use Illuminate\Database\Eloquent\Factories\Factory;
6
+
7
+ /**
8
+ * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Model>
9
+ */
10
+ class ListItemFactory extends Factory
11
+ {
12
+ /**
13
+ * Define the model's default state.
14
+ *
15
+ * @return array<string, mixed>
16
+ */
17
+ public function definition(): array
18
+ {
19
+ return [
20
+ //
21
+ 'name' => $this->faker->name(),
22
+ 'is_complete' => $this->faker->numberBetween($min = 0, $max = 1),
23
+ ];
24
+ }
25
+ }
pcov-1.0.11/package.xml ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <package packagerversion="1.10.13" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
3
+ <name>pcov</name>
4
+ <channel>pecl.php.net</channel>
5
+ <summary>Code coverage driver</summary>
6
+ <description>A self contained php-code-coverage compatible driver for PHP.</description>
7
+ <lead>
8
+ <name>Joe Watkins</name>
9
+ <user>krakjoe</user>
10
+ <email>[email protected]</email>
11
+ <active>yes</active>
12
+ </lead>
13
+ <lead>
14
+ <name>Remi Collet</name>
15
+ <user>remi</user>
16
+ <email>[email protected]</email>
17
+ <active>yes</active>
18
+ </lead>
19
+ <date>2021-12-20</date>
20
+ <time>16:52:09</time>
21
+ <version>
22
+ <release>1.0.11</release>
23
+ <api>1.0.0</api>
24
+ </version>
25
+ <stability>
26
+ <release>stable</release>
27
+ <api>stable</api>
28
+ </stability>
29
+ <license uri="http://www.php.net/license">PHP License</license>
30
+ <notes>
31
+ - Fix anonymous function coverage
32
+ </notes>
33
+ <contents>
34
+ <dir name="/">
35
+ <file md5sum="93f5486205626d6eba2d209b17bd193e" name="tests/001.phpt" role="test" />
36
+ <file md5sum="6b95706fb3f8dc5aa3d3bddf32a59f77" name="tests/002.phpt" role="test" />
37
+ <file md5sum="b3f1c66c5acdbf38f0e71bd4b68a113b" name="tests/003.phpt" role="test" />
38
+ <file md5sum="4a71f06f3c696ebba237f7cf384a6add" name="tests/004.phpt" role="test" />
39
+ <file md5sum="d01afd57c13e8d6c14eaba8f13580cfd" name="tests/005.phpt" role="test" />
40
+ <file md5sum="19ef5f87be894ea2e21741e11ff070e3" name="tests/006.phpt" role="test" />
41
+ <file md5sum="8a30bcf04d22dd4cedb596523fecd51d" name="tests/007.phpt" role="test" />
42
+ <file md5sum="dd62ffbecb188d3ba1dc2f1d75500471" name="cfg/701/zend_cfg.h" role="src" />
43
+ <file md5sum="87b970404e3031a02359d8a304a178cd" name="cfg/701/zend_cfg.c" role="src" />
44
+ <file md5sum="71191356d54ce9d0b092e32a1af93a89" name="cfg/701/zend_worklist.h" role="src" />
45
+ <file md5sum="3cfc353db84cbbe5a4cc6ee24e0d373f" name="cfg/702/zend_cfg.h" role="src" />
46
+ <file md5sum="0d0c5fd1d3e013b683ba96ddf095f8da" name="cfg/702/zend_cfg.c" role="src" />
47
+ <file md5sum="5e7ced31fffd27eb43086a9c2c4a0a79" name="cfg/702/zend_worklist.h" role="src" />
48
+ <file md5sum="558cb212089bf4be25ac65f315a2d336" name="cfg/703/zend_cfg.h" role="src" />
49
+ <file md5sum="1a0f67c3956478a2fc2b9085b67a2b33" name="cfg/703/zend_cfg.c" role="src" />
50
+ <file md5sum="e8d04cc62e08921dc37bceb54e21caf3" name="cfg/703/zend_worklist.h" role="src" />
51
+ <file md5sum="b33132efb5ea6c6ad6cb8c84b12be7ed" name="cfg/704/zend_cfg.h" role="src" />
52
+ <file md5sum="ddb9d10c26bfa5f6f386746257ec57db" name="cfg/704/zend_cfg.c" role="src" />
53
+ <file md5sum="f86293a5a150c9b208cb88edac66025f" name="cfg/704/zend_worklist.h" role="src" />
54
+ <file md5sum="fb07bfc51f6d5e0c30b65d9701233b2e" name="LICENSE" role="doc" />
55
+ <file md5sum="e5bd4101b1949bc39e45840bafcb22b7" name="README.md" role="doc" />
56
+ <file md5sum="a2c30f07ba7cf6353da075ca57609ceb" name="config.m4" role="src" />
57
+ <file md5sum="bdb0adc09350e458d4ecc60ea747f828" name="config.w32" role="src" />
58
+ <file md5sum="509fc4e7754da03938e61171ecce1573" name="php_pcov.h" role="src" />
59
+ <file md5sum="adb8dceda66e3f1c55d997963d6efae0" name="pcov.c" role="src" />
60
+ </dir>
61
+ </contents>
62
+ <dependencies>
63
+ <required>
64
+ <php>
65
+ <min>7.1.0</min>
66
+ </php>
67
+ <pearinstaller>
68
+ <min>1.10</min>
69
+ </pearinstaller>
70
+ </required>
71
+ </dependencies>
72
+ <providesextension>pcov</providesextension>
73
+ <extsrcrelease />
74
+ <changelog>
75
+ <release>
76
+ <date>2021-11-24</date>
77
+ <version>
78
+ <release>1.0.10</release>
79
+ <api>1.0.0</api>
80
+ </version>
81
+ <stability>
82
+ <release>stable</release>
83
+ <api>stable</api>
84
+ </stability>
85
+ <license uri="http://www.php.net/license">PHP License</license>
86
+ <notes>
87
+ - Use zend_new_interned_string instead of hand-rolling
88
+ - Use ZEND_MOD_END to fix build warning
89
+ </notes>
90
+ </release>
91
+ <release>
92
+ <date>2021-06-07</date>
93
+ <version>
94
+ <release>1.0.9</release>
95
+ <api>1.0.0</api>
96
+ </version>
97
+ <stability>
98
+ <release>stable</release>
99
+ <api>stable</api>
100
+ </stability>
101
+ <license uri="http://www.php.net/license">PHP License</license>
102
+ <notes>
103
+ - Fix #67 huge memory consumption to generate coverage
104
+ </notes>
105
+ </release>
106
+ <release>
107
+ <date>2021-03-22</date>
108
+ <version>
109
+ <release>1.0.8</release>
110
+ <api>1.0.0</api>
111
+ </version>
112
+ <stability>
113
+ <release>stable</release>
114
+ <api>stable</api>
115
+ </stability>
116
+ <license uri="http://www.php.net/license">PHP License</license>
117
+ <notes>
118
+ - Fix Windows build
119
+ </notes>
120
+ </release>
121
+ <release>
122
+ <date>2021-03-19</date>
123
+ <version>
124
+ <release>1.0.7</release>
125
+ <api>1.0.0</api>
126
+ </version>
127
+ <stability>
128
+ <release>stable</release>
129
+ <api>stable</api>
130
+ </stability>
131
+ <license uri="http://www.php.net/license">PHP License</license>
132
+ <notes>
133
+ - Fix #46 Segmentation fault on 7.4 on macOS with anonymous class
134
+ - Fix #59 &quot;malloc_consolidate(): invalid chunk size&quot; with PHP 8.1
135
+ </notes>
136
+ </release>
137
+ <release>
138
+ <date>2019-06-13</date>
139
+ <version>
140
+ <release>1.0.6</release>
141
+ <api>1.0.0</api>
142
+ </version>
143
+ <stability>
144
+ <release>stable</release>
145
+ <api>stable</api>
146
+ </stability>
147
+ <license uri="http://www.php.net/license">PHP License</license>
148
+ <notes>
149
+ - Fix for 7.4.0alpha1
150
+ </notes>
151
+ </release>
152
+ <release>
153
+ <date>2019-06-11</date>
154
+ <version>
155
+ <release>1.0.5</release>
156
+ <api>1.0.0</api>
157
+ </version>
158
+ <stability>
159
+ <release>stable</release>
160
+ <api>stable</api>
161
+ </stability>
162
+ <license uri="http://www.php.net/license">PHP License</license>
163
+ <notes>
164
+ - Fix #19 fault in 7.2 caused by modification of filename pointer
165
+ </notes>
166
+ </release>
167
+ <release>
168
+ <date>2019-06-03</date>
169
+ <version>
170
+ <release>1.0.4</release>
171
+ <api>1.0.0</api>
172
+ </version>
173
+ <stability>
174
+ <release>stable</release>
175
+ <api>stable</api>
176
+ </stability>
177
+ <license uri="http://www.php.net/license">PHP License</license>
178
+ <notes>
179
+ - Fix #17 Lines not covered when more tests are run
180
+ - Improve perf of clear routine
181
+ - Merge upstream cfg updates (switch block change)
182
+ - Omit ignored opcodes from internal coverage data completely (doesn&apos;t effect users, except less memory used)
183
+ </notes>
184
+ </release>
185
+ <release>
186
+ <date>2019-05-06</date>
187
+ <version>
188
+ <release>1.0.3</release>
189
+ <api>1.0.0</api>
190
+ </version>
191
+ <stability>
192
+ <release>stable</release>
193
+ <api>stable</api>
194
+ </stability>
195
+ <license uri="http://www.php.net/license">PHP License</license>
196
+ <notes>
197
+ - Fix #12 issue with faulty line coverage
198
+ </notes>
199
+ </release>
200
+ <release>
201
+ <date>2019-03-31</date>
202
+ <version>
203
+ <release>1.0.2</release>
204
+ <api>1.0.0</api>
205
+ </version>
206
+ <stability>
207
+ <release>stable</release>
208
+ <api>stable</api>
209
+ </stability>
210
+ <license uri="http://www.php.net/license">PHP License</license>
211
+ <notes>
212
+ - Fix #10 second non-cli SAPI request behaves strangely
213
+ </notes>
214
+ </release>
215
+ <release>
216
+ <date>2019-03-25</date>
217
+ <version>
218
+ <release>1.0.1</release>
219
+ <api>1.0.0</api>
220
+ </version>
221
+ <stability>
222
+ <release>stable</release>
223
+ <api>stable</api>
224
+ </stability>
225
+ <license uri="http://www.php.net/license">PHP License</license>
226
+ <notes>
227
+ - Release memory from building CFG as early as possible
228
+ - Compat with 7.4/8.0 changes
229
+ </notes>
230
+ </release>
231
+ <release>
232
+ <date>2019-01-30</date>
233
+ <version>
234
+ <release>1.0.0</release>
235
+ <api>1.0.0</api>
236
+ </version>
237
+ <stability>
238
+ <release>stable</release>
239
+ <api>stable</api>
240
+ </stability>
241
+ <license uri="http://www.php.net/license">PHP License</license>
242
+ <notes>
243
+ - initial pecl stable release
244
+ </notes>
245
+ </release>
246
+ <release>
247
+ <date>2019-01-22</date>
248
+ <version>
249
+ <release>0.9.0</release>
250
+ <api>0.9.0</api>
251
+ </version>
252
+ <stability>
253
+ <release>beta</release>
254
+ <api>beta</api>
255
+ </stability>
256
+ <license uri="http://www.php.net/license">PHP License</license>
257
+ <notes>
258
+ - initial pecl release
259
+ </notes>
260
+ </release>
261
+ </changelog>
262
+ </package>
pcov-1.0.11/pcov-1.0.11/LICENSE ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --------------------------------------------------------------------
2
+ The PHP License, version 3.01
3
+ Copyright (c) 1999 - 2018 The PHP Group. All rights reserved.
4
+ --------------------------------------------------------------------
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, is permitted provided that the following conditions
8
+ are met:
9
+
10
+ 1. Redistributions of source code must retain the above copyright
11
+ notice, this list of conditions and the following disclaimer.
12
+
13
+ 2. Redistributions in binary form must reproduce the above copyright
14
+ notice, this list of conditions and the following disclaimer in
15
+ the documentation and/or other materials provided with the
16
+ distribution.
17
+
18
+ 3. The name "PHP" must not be used to endorse or promote products
19
+ derived from this software without prior written permission. For
20
+ written permission, please contact [email protected].
21
+
22
+ 4. Products derived from this software may not be called "PHP", nor
23
+ may "PHP" appear in their name, without prior written permission
24
+ from [email protected]. You may indicate that your software works in
25
+ conjunction with PHP by saying "Foo for PHP" instead of calling
26
+ it "PHP Foo" or "phpfoo"
27
+
28
+ 5. The PHP Group may publish revised and/or new versions of the
29
+ license from time to time. Each version will be given a
30
+ distinguishing version number.
31
+ Once covered code has been published under a particular version
32
+ of the license, you may always continue to use it under the terms
33
+ of that version. You may also choose to use such covered code
34
+ under the terms of any subsequent version of the license
35
+ published by the PHP Group. No one other than the PHP Group has
36
+ the right to modify the terms applicable to covered code created
37
+ under this License.
38
+
39
+ 6. Redistributions of any form whatsoever must retain the following
40
+ acknowledgment:
41
+ "This product includes PHP software, freely available from
42
+ <http://www.php.net/software/>".
43
+
44
+ THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND
45
+ ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
46
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
47
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP
48
+ DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
49
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
50
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
51
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
53
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
54
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
55
+ OF THE POSSIBILITY OF SUCH DAMAGE.
56
+
57
+ --------------------------------------------------------------------
58
+
59
+ This software consists of voluntary contributions made by many
60
+ individuals on behalf of the PHP Group.
61
+
62
+ The PHP Group can be contacted via Email at [email protected].
63
+
64
+ For more information on the PHP Group and the PHP project,
65
+ please see <http://www.php.net>.
66
+
67
+ PHP includes the Zend Engine, freely available at
68
+ <http://www.zend.com>.
pcov-1.0.11/pcov-1.0.11/README.md ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ PCOV
2
+ ====
3
+
4
+ [![Build Status](https://travis-ci.org/krakjoe/pcov.svg?branch=develop)](https://travis-ci.org/krakjoe/pcov)
5
+ [![Build status](https://ci.appveyor.com/api/projects/status/w265n0w7yk6o3y6m?svg=true)](https://ci.appveyor.com/project/krakjoe/pcov)
6
+
7
+ A self contained [CodeCoverage](https://github.com/sebastianbergmann/php-code-coverage) compatible driver for PHP
8
+
9
+ Requirements and Installation
10
+ =============================
11
+
12
+ See [INSTALL.md](INSTALL.md)
13
+
14
+
15
+ API
16
+ ===
17
+
18
+ ```php
19
+ /**
20
+ * Shall start recording coverage information
21
+ */
22
+ function \pcov\start() : void;
23
+
24
+ /**
25
+ * Shall stop recording coverage information
26
+ */
27
+ function \pcov\stop() : void;
28
+
29
+ /**
30
+ * Shall collect coverage information
31
+ *
32
+ * @param integer $type define witch type of information should be collected
33
+ * \pcov\all shall collect coverage information for all files
34
+ * \pcov\inclusive shall collect coverage information for the specified files
35
+ * \pcov\exclusive shall collect coverage information for all but the specified files
36
+ * @param array $filter path of files (realpath) that should be filtered
37
+ *
38
+ * @return array
39
+ */
40
+ function \pcov\collect(int $type = \pcov\all, array $filter = []) : array;
41
+
42
+ /**
43
+ * Shall clear stored information
44
+ *
45
+ * @param bool $files set true to clear file tables
46
+ *
47
+ * Note: clearing the file tables may have surprising consequences
48
+ */
49
+ function \pcov\clear(bool $files = false) : void;
50
+
51
+ /**
52
+ * Shall return list of files waiting to be collected
53
+ */
54
+ function \pcov\waiting() : array;
55
+
56
+ /**
57
+ * Shall return the current size of the trace and cfg arena
58
+ */
59
+ function \pcov\memory() : int;
60
+ ```
61
+
62
+ Configuration
63
+ =============
64
+
65
+ PCOV is configured using PHP.ini:
66
+
67
+ | Option | Default | Changeable | Description |
68
+ |:-----------------------|:-------------------|:--------------:|:------------------------------------------------------|
69
+ | `pcov.enabled` | 1 | SYSTEM | enable or disable zend hooks for pcov |
70
+ | `pcov.directory` | auto | SYSTEM,PERDIR | restrict collection to files under this path |
71
+ | `pcov.exclude` | unused | SYSTEM,PERDIR | exclude files under pcov.directory matching this PCRE |
72
+ | `pcov.initial.memory` | 65536 | SYSTEM,PERDIR | shall set initial size of arena |
73
+ | `pcov.initial.files` | 64 | SYSTEM,PERDIR | shall set initial size of tables |
74
+
75
+ Notes
76
+ -----
77
+
78
+ The recommended defaults for production should be:
79
+
80
+ * `pcov.enabled = 0`
81
+
82
+ The recommended defaults for development should be:
83
+
84
+ * `pcov.enabled = 1`
85
+ * `pcov.directory = /path/to/your/source/directory`
86
+
87
+ When `pcov.directory` is left unset, PCOV will attempt to find `src`, `lib` or, `app` in the current
88
+ working directory, in that order; If none are found the current directory will be used, which may waste resources storing
89
+ coverage information for the test suite.
90
+
91
+ If `pcov.directory` contains test code, it's recommended to set `pcov.exclude` to avoid wasting resources.
92
+
93
+ To avoid unnecessary allocation of additional arenas for traces and control flow graphs, `pcov.initial.memory` should be set according to the memory required by the test suite, which may be discovered with `\pcov\memory()`.
94
+
95
+ To avoid reallocation of tables, `pcov.initial.files` should be set to a number higher than the number of files that will be loaded during testing, inclusive of test files.
96
+
97
+ *Note that arenas are allocated in chunks: If the chunk size is set to 65536 and pcov require 65537 bytes, the system will allocate two chunks, each 65536 bytes. When setting arena space therefore, be generous in your estimates.*
98
+
99
+ Interoperability
100
+ ================
101
+
102
+ When PCOV is enabled by configuration `pcov.enabled=1`:
103
+
104
+ * interoperability with Xdebug is not possible
105
+ * interoperability with phpdbg is not possible
106
+ * interoperability with Blackfire profiler is not possible
107
+
108
+ At an internals level, the executor function is overriden by pcov, so any extension or SAPI which does the same will be broken.
109
+
110
+ When PCOV is disabled by configuration `pcov.enabled=0`:
111
+
112
+ * PCOV is zero cost - code runs at full speed
113
+ * Xdebug may be loaded
114
+ * phpdbg may be executed
115
+ * Blackfire probe may be loaded
116
+
117
+ At an internals level, the executor function is untouched, and pcov allocates no memory.
118
+
119
+ Differences in Reporting
120
+ ========================
121
+
122
+ There are subtle differences between Xdebug and PCOV in reporting: Both Xdebug and PCOV perform branch analysis in order to detect executable code. Xdebug has custom written (very mature, proven) analysis, while PCOV uses the very well proven control flow graph from Optimizer. They generate comparably accurate reports, while phpdbg uses less robust detection of executable code and generates reports with known defects. One such defect in phpdbg is this:
123
+
124
+ ```php
125
+ /* 2 */ function foo($bar) {
126
+ /* 3 */ if ($bar) {
127
+ /* 4 */ return true;
128
+ /* 5 */ }
129
+ /* 6 */ }
130
+ ```
131
+
132
+ phpdbg will detect that this function is 100% covered when the first control path is taken, `if ($bar)`, because it cannot correctly detect which implicit return paths inserted by Zend at compile time are executable, and so chooses to ignore them all. While this may seem like a trivial difference to some, it means that the reports generated by phpdbg are not completely trustworthy.
133
+
134
+ While the accuracy of Xdebug and PCOV are comparable, the reports they generate are not precisely the same, one such example is the `switch` construct:
135
+
136
+ ```php
137
+ /* 2 */ switch ($condition) {
138
+ /* 3 */ case 1:
139
+ /* 4 */ return "PHP rox!";
140
+ /* 5 */ }
141
+ ```
142
+
143
+ From PHP 7.2.15 and PCOV 1.0, PCOV will detect the executability of the cases inside the switch body correctly, but will not detect line 2 (with the `switch` statement) as executable because Zend didn't output an executable opcode on that line. Xdebug's custom analysis doesn't use the same method and so will show an extra executable line on 2. Pre 7.2.15 and PCOV 1.0, the coverage of some switches is questionable as a result of the way Zend constructs the opcodes in the body of the switch - it may not execute them depending on a jump table optimization.
144
+
145
+ While Xdebug and PCOV both do the same kind of analysis of code, Xdebug is currently able to do more with that information than just generate accurate line coverage reports, it has path coverage and PCOV does not, although path coverage is not yet implemented (and probably won't be) by CodeCoverage.
146
+
147
+ Differences in Performance
148
+ ==========================
149
+
150
+ The differences in performance of Xdebug and PCOV are not slight. Xdebug is first and foremost a debugging extension, and when you load it, you incur the overhead of a debugger even when it's disabled. PCOV is less than 1000 lines of code (not including CFG) and doesn't have anything like the overhead of a debugger.
pcov-1.0.11/pcov-1.0.11/cfg/701/zend_cfg.c ADDED
@@ -0,0 +1,595 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ +----------------------------------------------------------------------+
3
+ | Zend Engine, CFG - Control Flow Graph |
4
+ +----------------------------------------------------------------------+
5
+ | Copyright (c) 1998-2018 The PHP Group |
6
+ +----------------------------------------------------------------------+
7
+ | This source file is subject to version 3.01 of the PHP license, |
8
+ | that is bundled with this package in the file LICENSE, and is |
9
+ | available through the world-wide-web at the following url: |
10
+ | http://www.php.net/license/3_01.txt |
11
+ | If you did not receive a copy of the PHP license and are unable to |
12
+ | obtain it through the world-wide-web, please send a note to |
13
+ | [email protected] so we can mail you a copy immediately. |
14
+ +----------------------------------------------------------------------+
15
+ | Authors: Dmitry Stogov <[email protected]> |
16
+ +----------------------------------------------------------------------+
17
+
18
+ This source file has been adapted for pcov so that the CFG from O+ is standalone
19
+ */
20
+
21
+ #include "php.h"
22
+ #include "zend_compile.h"
23
+ #include "zend_cfg.h"
24
+ #include "zend_worklist.h"
25
+
26
+ /* func flags */
27
+ #define ZEND_FUNC_INDIRECT_VAR_ACCESS (1<<0) /* accesses variables by name */
28
+ #define ZEND_FUNC_HAS_CALLS (1<<1)
29
+ #define ZEND_FUNC_VARARG (1<<2) /* uses func_get_args() */
30
+ #define ZEND_FUNC_NO_LOOPS (1<<3)
31
+ #define ZEND_FUNC_IRREDUCIBLE (1<<4)
32
+ #define ZEND_FUNC_RECURSIVE (1<<7)
33
+ #define ZEND_FUNC_RECURSIVE_DIRECTLY (1<<8)
34
+ #define ZEND_FUNC_RECURSIVE_INDIRECTLY (1<<9)
35
+ #define ZEND_FUNC_HAS_EXTENDED_INFO (1<<10)
36
+
37
+ /* The following flags are valid only for return values of internal functions
38
+ * returned by zend_get_func_info()
39
+ */
40
+ #define FUNC_MAY_WARN (1<<30)
41
+
42
+ typedef struct _zend_func_info zend_func_info;
43
+ typedef struct _zend_call_info zend_call_info;
44
+
45
+ #define ZEND_FUNC_INFO(op_array) \
46
+ ((zend_func_info*)((op_array)->reserved[zend_func_info_rid]))
47
+
48
+ #define ZEND_SET_FUNC_INFO(op_array, info) do { \
49
+ zend_func_info** pinfo = (zend_func_info**)&(op_array)->reserved[zend_func_info_rid]; \
50
+ *pinfo = info; \
51
+ } while (0)
52
+
53
+ static uint32_t zend_cfg_classify_function(zend_string *name, uint32_t num_args) {
54
+ if (zend_string_equals_literal(name, "extract")) {
55
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
56
+ } else if (zend_string_equals_literal(name, "compact")) {
57
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
58
+ } else if (zend_string_equals_literal(name, "parse_str") && num_args <= 1) {
59
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
60
+ } else if (zend_string_equals_literal(name, "mb_parse_str") && num_args <= 1) {
61
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
62
+ } else if (zend_string_equals_literal(name, "get_defined_vars")) {
63
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
64
+ } else if (zend_string_equals_literal(name, "assert")) {
65
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
66
+ } else if (zend_string_equals_literal(name, "func_num_args")) {
67
+ return ZEND_FUNC_VARARG;
68
+ } else if (zend_string_equals_literal(name, "func_get_arg")) {
69
+ return ZEND_FUNC_VARARG;
70
+ } else if (zend_string_equals_literal(name, "func_get_args")) {
71
+ return ZEND_FUNC_VARARG;
72
+ } else {
73
+ return 0;
74
+ }
75
+ }
76
+
77
+ static void zend_mark_reachable(zend_op *opcodes, zend_cfg *cfg, zend_basic_block *b) /* {{{ */
78
+ {
79
+ zend_uchar opcode;
80
+ zend_basic_block *b0;
81
+ int successor_0, successor_1;
82
+ zend_basic_block *blocks = cfg->blocks;
83
+
84
+ while (1) {
85
+ b->flags |= ZEND_BB_REACHABLE;
86
+ successor_0 = b->successors[0];
87
+ if (successor_0 >= 0) {
88
+ successor_1 = b->successors[1];
89
+ if (successor_1 >= 0) {
90
+ b0 = blocks + successor_0;
91
+ b0->flags |= ZEND_BB_TARGET;
92
+ if (!(b0->flags & ZEND_BB_REACHABLE)) {
93
+ zend_mark_reachable(opcodes, cfg, b0);
94
+ }
95
+
96
+ ZEND_ASSERT(b->len != 0);
97
+ opcode = opcodes[b->start + b->len - 1].opcode;
98
+ b = blocks + successor_1;
99
+ if (opcode == ZEND_JMPZNZ) {
100
+ b->flags |= ZEND_BB_TARGET;
101
+ } else {
102
+ b->flags |= ZEND_BB_FOLLOW;
103
+ }
104
+ } else if (b->len != 0) {
105
+ opcode = opcodes[b->start + b->len - 1].opcode;
106
+ b = blocks + successor_0;
107
+ if (opcode == ZEND_JMP) {
108
+ b->flags |= ZEND_BB_TARGET;
109
+ } else {
110
+ b->flags |= ZEND_BB_FOLLOW;
111
+
112
+ if (cfg->split_at_calls) {
113
+ if (opcode == ZEND_INCLUDE_OR_EVAL ||
114
+ opcode == ZEND_GENERATOR_CREATE ||
115
+ opcode == ZEND_YIELD ||
116
+ opcode == ZEND_YIELD_FROM ||
117
+ opcode == ZEND_DO_FCALL ||
118
+ opcode == ZEND_DO_UCALL ||
119
+ opcode == ZEND_DO_FCALL_BY_NAME) {
120
+ b->flags |= ZEND_BB_ENTRY;
121
+ }
122
+ }
123
+ if (cfg->split_at_recv) {
124
+ if (opcode == ZEND_RECV ||
125
+ opcode == ZEND_RECV_INIT) {
126
+ b->flags |= ZEND_BB_RECV_ENTRY;
127
+ }
128
+ }
129
+ }
130
+ } else {
131
+ b = blocks + successor_0;
132
+ b->flags |= ZEND_BB_FOLLOW;
133
+ }
134
+ if (b->flags & ZEND_BB_REACHABLE) return;
135
+ } else {
136
+ b->flags |= ZEND_BB_EXIT;
137
+ return;
138
+ }
139
+ }
140
+ }
141
+ /* }}} */
142
+
143
+ static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg, int start) /* {{{ */
144
+ {
145
+ zend_basic_block *blocks = cfg->blocks;
146
+
147
+ blocks[start].flags = ZEND_BB_START;
148
+ zend_mark_reachable(op_array->opcodes, cfg, blocks + start);
149
+
150
+ if (op_array->last_live_range || op_array->last_try_catch) {
151
+ zend_basic_block *b;
152
+ int j, changed;
153
+ uint32_t *block_map = cfg->map;
154
+
155
+ do {
156
+ changed = 0;
157
+
158
+ /* Add live range paths */
159
+ for (j = 0; j < op_array->last_live_range; j++) {
160
+ zend_live_range *live_range = &op_array->live_range[j];
161
+ if (live_range->var == (uint32_t)-1) {
162
+ /* this live range already removed */
163
+ continue;
164
+ }
165
+ b = blocks + block_map[live_range->start];
166
+ if (b->flags & ZEND_BB_REACHABLE) {
167
+ while (b->len > 0 && op_array->opcodes[b->start].opcode == ZEND_NOP) {
168
+ /* check if NOP breaks incorrect smart branch */
169
+ if (b->len == 2
170
+ && (op_array->opcodes[b->start + 1].opcode == ZEND_JMPZ
171
+ || op_array->opcodes[b->start + 1].opcode == ZEND_JMPNZ)
172
+ && (op_array->opcodes[b->start + 1].op1_type & (IS_CV|IS_CONST))
173
+ && b->start > 0
174
+ && zend_is_smart_branch(op_array->opcodes + b->start - 1)) {
175
+ break;
176
+ }
177
+ b->start++;
178
+ b->len--;
179
+ }
180
+ if (b->len == 0 && (uint32_t)b->successors[0] == block_map[live_range->end]) {
181
+ /* mark as removed (empty live range) */
182
+ live_range->var = (uint32_t)-1;
183
+ continue;
184
+ }
185
+ b->flags |= ZEND_BB_GEN_VAR;
186
+ b = blocks + block_map[live_range->end];
187
+ b->flags |= ZEND_BB_KILL_VAR;
188
+ if (!(b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE))) {
189
+ if (cfg->split_at_live_ranges) {
190
+ changed = 1;
191
+ zend_mark_reachable(op_array->opcodes, cfg, b);
192
+ } else {
193
+ ZEND_ASSERT(b->start == live_range->end);
194
+ b->flags |= ZEND_BB_UNREACHABLE_FREE;
195
+ }
196
+ }
197
+ } else {
198
+ ZEND_ASSERT(!(blocks[block_map[live_range->end]].flags & ZEND_BB_REACHABLE));
199
+ }
200
+ }
201
+
202
+ /* Add exception paths */
203
+ for (j = 0; j < op_array->last_try_catch; j++) {
204
+
205
+ /* check for jumps into the middle of try block */
206
+ b = blocks + block_map[op_array->try_catch_array[j].try_op];
207
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
208
+ zend_basic_block *end;
209
+
210
+ if (op_array->try_catch_array[j].catch_op) {
211
+ end = blocks + block_map[op_array->try_catch_array[j].catch_op];
212
+ while (b != end) {
213
+ if (b->flags & ZEND_BB_REACHABLE) {
214
+ op_array->try_catch_array[j].try_op = b->start;
215
+ break;
216
+ }
217
+ b++;
218
+ }
219
+ }
220
+ b = blocks + block_map[op_array->try_catch_array[j].try_op];
221
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
222
+ if (op_array->try_catch_array[j].finally_op) {
223
+ end = blocks + block_map[op_array->try_catch_array[j].finally_op];
224
+ while (b != end) {
225
+ if (b->flags & ZEND_BB_REACHABLE) {
226
+ op_array->try_catch_array[j].try_op = op_array->try_catch_array[j].catch_op;
227
+ changed = 1;
228
+ zend_mark_reachable(op_array->opcodes, cfg, blocks + block_map[op_array->try_catch_array[j].try_op]);
229
+ break;
230
+ }
231
+ b++;
232
+ }
233
+ }
234
+ }
235
+ }
236
+
237
+ b = blocks + block_map[op_array->try_catch_array[j].try_op];
238
+ if (b->flags & ZEND_BB_REACHABLE) {
239
+ b->flags |= ZEND_BB_TRY;
240
+ if (op_array->try_catch_array[j].catch_op) {
241
+ b = blocks + block_map[op_array->try_catch_array[j].catch_op];
242
+ b->flags |= ZEND_BB_CATCH;
243
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
244
+ changed = 1;
245
+ zend_mark_reachable(op_array->opcodes, cfg, b);
246
+ }
247
+ }
248
+ if (op_array->try_catch_array[j].finally_op) {
249
+ b = blocks + block_map[op_array->try_catch_array[j].finally_op];
250
+ b->flags |= ZEND_BB_FINALLY;
251
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
252
+ changed = 1;
253
+ zend_mark_reachable(op_array->opcodes, cfg, b);
254
+ }
255
+ }
256
+ if (op_array->try_catch_array[j].finally_end) {
257
+ b = blocks + block_map[op_array->try_catch_array[j].finally_end];
258
+ b->flags |= ZEND_BB_FINALLY_END;
259
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
260
+ changed = 1;
261
+ zend_mark_reachable(op_array->opcodes, cfg, b);
262
+ }
263
+ }
264
+ } else {
265
+ if (op_array->try_catch_array[j].catch_op) {
266
+ ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].catch_op]].flags & ZEND_BB_REACHABLE));
267
+ }
268
+ if (op_array->try_catch_array[j].finally_op) {
269
+ ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_op]].flags & ZEND_BB_REACHABLE));
270
+ }
271
+ if (op_array->try_catch_array[j].finally_end) {
272
+ ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_end]].flags & ZEND_BB_REACHABLE));
273
+ }
274
+ }
275
+ }
276
+ } while (changed);
277
+ }
278
+ }
279
+ /* }}} */
280
+
281
+ static void record_successor(zend_basic_block *blocks, int pred, int n, int succ)
282
+ {
283
+ blocks[pred].successors[n] = succ;
284
+ }
285
+
286
+ static void initialize_block(zend_basic_block *block) {
287
+ block->flags = 0;
288
+ block->successors[0] = -1;
289
+ block->successors[1] = -1;
290
+ block->predecessors_count = 0;
291
+ block->predecessor_offset = -1;
292
+ block->idom = -1;
293
+ block->loop_header = -1;
294
+ block->level = -1;
295
+ block->children = -1;
296
+ block->next_child = -1;
297
+ }
298
+
299
+ #define BB_START(i) do { \
300
+ if (!block_map[i]) { blocks_count++;} \
301
+ block_map[i]++; \
302
+ } while (0)
303
+
304
+ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg) /* {{{ */
305
+ {
306
+ uint32_t flags = 0;
307
+ uint32_t i;
308
+ int j;
309
+ uint32_t *block_map;
310
+ zend_function *fn;
311
+ int blocks_count = 0;
312
+ zend_basic_block *blocks;
313
+ zval *zv;
314
+ zend_bool extra_entry_block = 0;
315
+
316
+ cfg->split_at_live_ranges = (build_flags & ZEND_CFG_SPLIT_AT_LIVE_RANGES) != 0;
317
+ cfg->split_at_calls = (build_flags & ZEND_CFG_STACKLESS) != 0;
318
+ cfg->split_at_recv = (build_flags & ZEND_CFG_RECV_ENTRY) != 0 && (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0;
319
+
320
+ cfg->map = block_map = zend_arena_calloc(arena, op_array->last, sizeof(uint32_t));
321
+ if (!block_map) {
322
+ return FAILURE;
323
+ }
324
+
325
+ /* Build CFG, Step 1: Find basic blocks starts, calculate number of blocks */
326
+ BB_START(0);
327
+ for (i = 0; i < op_array->last; i++) {
328
+ zend_op *opline = op_array->opcodes + i;
329
+ switch(opline->opcode) {
330
+ case ZEND_RECV:
331
+ case ZEND_RECV_INIT:
332
+ if (build_flags & ZEND_CFG_RECV_ENTRY) {
333
+ BB_START(i + 1);
334
+ }
335
+ break;
336
+ case ZEND_RETURN:
337
+ case ZEND_RETURN_BY_REF:
338
+ case ZEND_GENERATOR_RETURN:
339
+ case ZEND_EXIT:
340
+ case ZEND_THROW:
341
+ if (i + 1 < op_array->last) {
342
+ BB_START(i + 1);
343
+ }
344
+ break;
345
+ case ZEND_INCLUDE_OR_EVAL:
346
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
347
+ case ZEND_GENERATOR_CREATE:
348
+ case ZEND_YIELD:
349
+ case ZEND_YIELD_FROM:
350
+ if (build_flags & ZEND_CFG_STACKLESS) {
351
+ BB_START(i + 1);
352
+ }
353
+ break;
354
+ case ZEND_DO_FCALL:
355
+ case ZEND_DO_UCALL:
356
+ case ZEND_DO_FCALL_BY_NAME:
357
+ flags |= ZEND_FUNC_HAS_CALLS;
358
+ if (build_flags & ZEND_CFG_STACKLESS) {
359
+ BB_START(i + 1);
360
+ }
361
+ break;
362
+ case ZEND_DO_ICALL:
363
+ flags |= ZEND_FUNC_HAS_CALLS;
364
+ break;
365
+ case ZEND_INIT_FCALL:
366
+ case ZEND_INIT_NS_FCALL_BY_NAME:
367
+ zv = CRT_CONSTANT(opline->op2);
368
+ if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
369
+ /* The third literal is the lowercased unqualified name */
370
+ zv += 2;
371
+ }
372
+ if ((fn = zend_hash_find_ptr(EG(function_table), Z_STR_P(zv))) != NULL) {
373
+ if (fn->type == ZEND_INTERNAL_FUNCTION) {
374
+ flags |= zend_cfg_classify_function(
375
+ Z_STR_P(zv), opline->extended_value);
376
+ }
377
+ }
378
+ break;
379
+ case ZEND_FAST_CALL:
380
+ BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
381
+ BB_START(i + 1);
382
+ break;
383
+ case ZEND_FAST_RET:
384
+ if (i + 1 < op_array->last) {
385
+ BB_START(i + 1);
386
+ }
387
+ break;
388
+ case ZEND_JMP:
389
+ BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
390
+ if (i + 1 < op_array->last) {
391
+ BB_START(i + 1);
392
+ }
393
+ break;
394
+ case ZEND_JMPZNZ:
395
+ BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
396
+ BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
397
+ if (i + 1 < op_array->last) {
398
+ BB_START(i + 1);
399
+ }
400
+ break;
401
+ case ZEND_JMPZ:
402
+ case ZEND_JMPNZ:
403
+ case ZEND_JMPZ_EX:
404
+ case ZEND_JMPNZ_EX:
405
+ case ZEND_JMP_SET:
406
+ case ZEND_COALESCE:
407
+ case ZEND_ASSERT_CHECK:
408
+ BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
409
+ BB_START(i + 1);
410
+ break;
411
+ case ZEND_CATCH:
412
+ if (!opline->result.num) {
413
+ BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
414
+ }
415
+ BB_START(i + 1);
416
+ break;
417
+ case ZEND_DECLARE_ANON_CLASS:
418
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
419
+ case ZEND_FE_FETCH_R:
420
+ case ZEND_FE_FETCH_RW:
421
+ BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
422
+ BB_START(i + 1);
423
+ break;
424
+ case ZEND_FE_RESET_R:
425
+ case ZEND_FE_RESET_RW:
426
+ BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
427
+ BB_START(i + 1);
428
+ break;
429
+ case ZEND_UNSET_VAR:
430
+ case ZEND_ISSET_ISEMPTY_VAR:
431
+ if (((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_LOCAL) &&
432
+ !(opline->extended_value & ZEND_QUICK_SET)) {
433
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
434
+ } else if (((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL ||
435
+ (opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL_LOCK) &&
436
+ !op_array->function_name) {
437
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
438
+ }
439
+ break;
440
+ case ZEND_FETCH_R:
441
+ case ZEND_FETCH_W:
442
+ case ZEND_FETCH_RW:
443
+ case ZEND_FETCH_FUNC_ARG:
444
+ case ZEND_FETCH_IS:
445
+ case ZEND_FETCH_UNSET:
446
+ if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_LOCAL) {
447
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
448
+ } else if (((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL ||
449
+ (opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL_LOCK) &&
450
+ !op_array->function_name) {
451
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
452
+ }
453
+ break;
454
+ }
455
+ }
456
+
457
+ /* If the entry block has predecessors, we may need to split it */
458
+ if ((build_flags & ZEND_CFG_NO_ENTRY_PREDECESSORS)
459
+ && op_array->last > 0 && block_map[0] > 1) {
460
+ extra_entry_block = 1;
461
+ }
462
+
463
+ if (cfg->split_at_live_ranges) {
464
+ for (j = 0; j < op_array->last_live_range; j++) {
465
+ BB_START(op_array->live_range[j].start);
466
+ BB_START(op_array->live_range[j].end);
467
+ }
468
+ }
469
+
470
+ if (op_array->last_try_catch) {
471
+ for (j = 0; j < op_array->last_try_catch; j++) {
472
+ BB_START(op_array->try_catch_array[j].try_op);
473
+ if (op_array->try_catch_array[j].catch_op) {
474
+ BB_START(op_array->try_catch_array[j].catch_op);
475
+ }
476
+ if (op_array->try_catch_array[j].finally_op) {
477
+ BB_START(op_array->try_catch_array[j].finally_op);
478
+ }
479
+ if (op_array->try_catch_array[j].finally_end) {
480
+ BB_START(op_array->try_catch_array[j].finally_end);
481
+ }
482
+ }
483
+ }
484
+
485
+ blocks_count += extra_entry_block;
486
+ cfg->blocks_count = blocks_count;
487
+
488
+ /* Build CFG, Step 2: Build Array of Basic Blocks */
489
+ cfg->blocks = blocks = zend_arena_calloc(arena, sizeof(zend_basic_block), blocks_count);
490
+ if (!blocks) {
491
+ return FAILURE;
492
+ }
493
+
494
+ blocks_count = -1;
495
+
496
+ if (extra_entry_block) {
497
+ initialize_block(&blocks[0]);
498
+ blocks[0].start = 0;
499
+ blocks[0].len = 0;
500
+ blocks_count++;
501
+ }
502
+
503
+ for (i = 0; i < op_array->last; i++) {
504
+ if (block_map[i]) {
505
+ if (blocks_count >= 0) {
506
+ blocks[blocks_count].len = i - blocks[blocks_count].start;
507
+ }
508
+ blocks_count++;
509
+ initialize_block(&blocks[blocks_count]);
510
+ blocks[blocks_count].start = i;
511
+ }
512
+ block_map[i] = blocks_count;
513
+ }
514
+
515
+ blocks[blocks_count].len = i - blocks[blocks_count].start;
516
+ blocks_count++;
517
+
518
+ /* Build CFG, Step 3: Calculate successors */
519
+ for (j = 0; j < blocks_count; j++) {
520
+ zend_op *opline;
521
+ if (blocks[j].len == 0) {
522
+ record_successor(blocks, j, 0, j + 1);
523
+ continue;
524
+ }
525
+
526
+ opline = op_array->opcodes + blocks[j].start + blocks[j].len - 1;
527
+ switch (opline->opcode) {
528
+ case ZEND_FAST_RET:
529
+ case ZEND_RETURN:
530
+ case ZEND_RETURN_BY_REF:
531
+ case ZEND_GENERATOR_RETURN:
532
+ case ZEND_EXIT:
533
+ case ZEND_THROW:
534
+ break;
535
+ case ZEND_JMP:
536
+ record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes]);
537
+ break;
538
+ case ZEND_JMPZNZ:
539
+ record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]);
540
+ record_successor(blocks, j, 1, block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
541
+ break;
542
+ case ZEND_JMPZ:
543
+ case ZEND_JMPNZ:
544
+ case ZEND_JMPZ_EX:
545
+ case ZEND_JMPNZ_EX:
546
+ case ZEND_JMP_SET:
547
+ case ZEND_COALESCE:
548
+ case ZEND_ASSERT_CHECK:
549
+ record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]);
550
+ record_successor(blocks, j, 1, j + 1);
551
+ break;
552
+ case ZEND_CATCH:
553
+ if (!opline->result.num) {
554
+ record_successor(blocks, j, 0, block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
555
+ record_successor(blocks, j, 1, j + 1);
556
+ } else {
557
+ record_successor(blocks, j, 0, j + 1);
558
+ }
559
+ break;
560
+ case ZEND_DECLARE_ANON_CLASS:
561
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
562
+ case ZEND_FE_FETCH_R:
563
+ case ZEND_FE_FETCH_RW:
564
+ record_successor(blocks, j, 0, block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
565
+ record_successor(blocks, j, 1, j + 1);
566
+ break;
567
+ case ZEND_FE_RESET_R:
568
+ case ZEND_FE_RESET_RW:
569
+ record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]);
570
+ record_successor(blocks, j, 1, j + 1);
571
+ break;
572
+ case ZEND_FAST_CALL:
573
+ record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes]);
574
+ record_successor(blocks, j, 1, j + 1);
575
+ break;
576
+ default:
577
+ record_successor(blocks, j, 0, j + 1);
578
+ break;
579
+ }
580
+ }
581
+
582
+ /* Build CFG, Step 4, Mark Reachable Basic Blocks */
583
+ zend_mark_reachable_blocks(op_array, cfg, 0);
584
+
585
+ return SUCCESS;
586
+ }
587
+ /* }}} */
588
+
589
+ /*
590
+ * Local variables:
591
+ * tab-width: 4
592
+ * c-basic-offset: 4
593
+ * indent-tabs-mode: t
594
+ * End:
595
+ */
pcov-1.0.11/pcov-1.0.11/cfg/701/zend_cfg.h ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ +----------------------------------------------------------------------+
3
+ | Zend Engine, CFG - Control Flow Graph |
4
+ +----------------------------------------------------------------------+
5
+ | Copyright (c) 1998-2018 The PHP Group |
6
+ +----------------------------------------------------------------------+
7
+ | This source file is subject to version 3.01 of the PHP license, |
8
+ | that is bundled with this package in the file LICENSE, and is |
9
+ | available through the world-wide-web at the following url: |
10
+ | http://www.php.net/license/3_01.txt |
11
+ | If you did not receive a copy of the PHP license and are unable to |
12
+ | obtain it through the world-wide-web, please send a note to |
13
+ | [email protected] so we can mail you a copy immediately. |
14
+ +----------------------------------------------------------------------+
15
+ | Authors: Dmitry Stogov <[email protected]> |
16
+ +----------------------------------------------------------------------+
17
+ */
18
+
19
+ #ifndef ZEND_CFG_H
20
+ #define ZEND_CFG_H
21
+
22
+ /* zend_basic_bloc.flags */
23
+ #define ZEND_BB_START (1<<0) /* fist block */
24
+ #define ZEND_BB_FOLLOW (1<<1) /* follows the next block */
25
+ #define ZEND_BB_TARGET (1<<2) /* jump taget */
26
+ #define ZEND_BB_EXIT (1<<3) /* without successors */
27
+ #define ZEND_BB_ENTRY (1<<4) /* stackless entry */
28
+ #define ZEND_BB_TRY (1<<5) /* start of try block */
29
+ #define ZEND_BB_CATCH (1<<6) /* start of catch block */
30
+ #define ZEND_BB_FINALLY (1<<7) /* start of finally block */
31
+ #define ZEND_BB_FINALLY_END (1<<8) /* end of finally block */
32
+ #define ZEND_BB_GEN_VAR (1<<9) /* start of live range */
33
+ #define ZEND_BB_KILL_VAR (1<<10) /* end of live range */
34
+ #define ZEND_BB_UNREACHABLE_FREE (1<<11) /* unreachable loop free */
35
+ #define ZEND_BB_RECV_ENTRY (1<<12) /* RECV entry */
36
+
37
+ #define ZEND_BB_LOOP_HEADER (1<<16)
38
+ #define ZEND_BB_IRREDUCIBLE_LOOP (1<<17)
39
+
40
+ #define ZEND_BB_REACHABLE (1<<31)
41
+
42
+ #define ZEND_BB_PROTECTED (ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY|ZEND_BB_TRY|ZEND_BB_CATCH|ZEND_BB_FINALLY|ZEND_BB_FINALLY_END|ZEND_BB_GEN_VAR|ZEND_BB_KILL_VAR)
43
+
44
+ typedef struct _zend_basic_block {
45
+ uint32_t flags;
46
+ uint32_t start; /* first opcode number */
47
+ uint32_t len; /* number of opcodes */
48
+ int successors[2]; /* up to 2 successor blocks */
49
+ int predecessors_count; /* number of predecessors */
50
+ int predecessor_offset; /* offset of 1-st predecessor */
51
+ int idom; /* immediate dominator block */
52
+ int loop_header; /* closest loop header, or -1 */
53
+ int level; /* steps away from the entry in the dom. tree */
54
+ int children; /* list of dominated blocks */
55
+ int next_child; /* next dominated block */
56
+ } zend_basic_block;
57
+
58
+ /*
59
+ +------------+---+---+---+---+---+
60
+ | |OP1|OP2|EXT| 0 | 1 |
61
+ +------------+---+---+---+---+---+
62
+ |JMP |ADR| | |OP1| - |
63
+ |JMPZ | |ADR| |OP2|FOL|
64
+ |JMPNZ | |ADR| |OP2|FOL|
65
+ |JMPZNZ | |ADR|ADR|OP2|EXT|
66
+ |JMPZ_EX | |ADR| |OP2|FOL|
67
+ |JMPNZ_EX | |ADR| |OP2|FOL|
68
+ |JMP_SET | |ADR| |OP2|FOL|
69
+ |COALESCE | |ADR| |OP2|FOL|
70
+ |ASSERT_CHK | |ADR| |OP2|FOL|
71
+ |NEW | |ADR| |OP2|FOL|
72
+ |DCL_ANON* |ADR| | |OP1|FOL|
73
+ |FE_RESET_* | |ADR| |OP2|FOL|
74
+ |FE_FETCH_* | | |ADR|EXT|FOL|
75
+ |CATCH | | |ADR|EXT|FOL|
76
+ |FAST_CALL |ADR| | |OP1|FOL|
77
+ |FAST_RET | | | | - | - |
78
+ |RETURN* | | | | - | - |
79
+ |EXIT | | | | - | - |
80
+ |THROW | | | | - | - |
81
+ |* | | | |FOL| - |
82
+ +------------+---+---+---+---+---+
83
+ */
84
+
85
+ typedef struct _zend_cfg {
86
+ int blocks_count; /* number of basic blocks */
87
+ zend_basic_block *blocks; /* array of basic blocks */
88
+ int *predecessors;
89
+ uint32_t *map;
90
+ unsigned int split_at_live_ranges : 1;
91
+ unsigned int split_at_calls : 1;
92
+ unsigned int split_at_recv : 1;
93
+ } zend_cfg;
94
+
95
+ /* Build Flags */
96
+ #define ZEND_RT_CONSTANTS (1<<31)
97
+ #define ZEND_CFG_STACKLESS (1<<30)
98
+ #define ZEND_SSA_DEBUG_LIVENESS (1<<29)
99
+ #define ZEND_SSA_DEBUG_PHI_PLACEMENT (1<<28)
100
+ #define ZEND_SSA_RC_INFERENCE (1<<27)
101
+ #define ZEND_CFG_SPLIT_AT_LIVE_RANGES (1<<26)
102
+ #define ZEND_CFG_NO_ENTRY_PREDECESSORS (1<<25)
103
+ #define ZEND_CFG_RECV_ENTRY (1<<24)
104
+ #define ZEND_CALL_TREE (1<<23)
105
+
106
+ #define CRT_CONSTANT_EX(op_array, node, rt_constants) \
107
+ ((rt_constants) ? \
108
+ RT_CONSTANT(op_array, (node)) \
109
+ : \
110
+ CT_CONSTANT_EX(op_array, (node).constant) \
111
+ )
112
+
113
+ #define CRT_CONSTANT(node) \
114
+ CRT_CONSTANT_EX(op_array, node, (build_flags & ZEND_RT_CONSTANTS))
115
+
116
+ #define RETURN_VALUE_USED(opline) \
117
+ ((opline)->result_type != IS_UNUSED)
118
+
119
+ BEGIN_EXTERN_C()
120
+
121
+ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg);
122
+
123
+ END_EXTERN_C()
124
+
125
+ #endif /* ZEND_CFG_H */
126
+
127
+ /*
128
+ * Local variables:
129
+ * tab-width: 4
130
+ * c-basic-offset: 4
131
+ * indent-tabs-mode: t
132
+ * End:
133
+ */
pcov-1.0.11/pcov-1.0.11/cfg/701/zend_worklist.h ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ +----------------------------------------------------------------------+
3
+ | Zend Engine |
4
+ +----------------------------------------------------------------------+
5
+ | Copyright (c) 1998-2018 The PHP Group |
6
+ +----------------------------------------------------------------------+
7
+ | This source file is subject to version 3.01 of the PHP license, |
8
+ | that is bundled with this package in the file LICENSE, and is |
9
+ | available through the world-wide-web at the following url: |
10
+ | http://www.php.net/license/3_01.txt |
11
+ | If you did not receive a copy of the PHP license and are unable to |
12
+ | obtain it through the world-wide-web, please send a note to |
13
+ | [email protected] so we can mail you a copy immediately. |
14
+ +----------------------------------------------------------------------+
15
+ | Authors: Andy Wingo <[email protected]> |
16
+ +----------------------------------------------------------------------+
17
+ */
18
+
19
+ /* $Id:$ */
20
+
21
+ #ifndef _ZEND_WORKLIST_H_
22
+ #define _ZEND_WORKLIST_H_
23
+
24
+ #include "zend_arena.h"
25
+ #include "zend_bitset.h"
26
+
27
+ typedef struct _zend_worklist_stack {
28
+ int *buf;
29
+ int len;
30
+ int capacity;
31
+ } zend_worklist_stack;
32
+
33
+ #define ZEND_WORKLIST_STACK_ALLOCA(s, _len, use_heap) do { \
34
+ (s)->buf = (int*)do_alloca(sizeof(int) * _len, use_heap); \
35
+ (s)->len = 0; \
36
+ (s)->capacity = _len; \
37
+ } while (0)
38
+
39
+ #define ZEND_WORKLIST_STACK_FREE_ALLOCA(s, use_heap) \
40
+ free_alloca((s)->buf, use_heap)
41
+
42
+ static inline int zend_worklist_stack_prepare(zend_arena **arena, zend_worklist_stack *stack, int len)
43
+ {
44
+ ZEND_ASSERT(len >= 0);
45
+
46
+ stack->buf = (int*)zend_arena_calloc(arena, sizeof(*stack->buf), len);
47
+ if (!stack->buf) {
48
+ return FAILURE;
49
+ }
50
+ stack->len = 0;
51
+ stack->capacity = len;
52
+
53
+ return SUCCESS;
54
+ }
55
+
56
+ static inline void zend_worklist_stack_push(zend_worklist_stack *stack, int i)
57
+ {
58
+ ZEND_ASSERT(stack->len < stack->capacity);
59
+ stack->buf[stack->len++] = i;
60
+ }
61
+
62
+ static inline int zend_worklist_stack_peek(zend_worklist_stack *stack)
63
+ {
64
+ ZEND_ASSERT(stack->len);
65
+ return stack->buf[stack->len - 1];
66
+ }
67
+
68
+ static inline int zend_worklist_stack_pop(zend_worklist_stack *stack)
69
+ {
70
+ ZEND_ASSERT(stack->len);
71
+ return stack->buf[--stack->len];
72
+ }
73
+
74
+ typedef struct _zend_worklist {
75
+ zend_bitset visited;
76
+ zend_worklist_stack stack;
77
+ } zend_worklist;
78
+
79
+ #define ZEND_WORKLIST_ALLOCA(w, _len, use_heap) do { \
80
+ (w)->stack.buf = (int*)do_alloca(ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len) + sizeof(zend_ulong) * zend_bitset_len(_len), use_heap); \
81
+ (w)->stack.len = 0; \
82
+ (w)->stack.capacity = _len; \
83
+ (w)->visited = (zend_bitset)((char*)(w)->stack.buf + ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len)); \
84
+ memset((w)->visited, 0, sizeof(zend_ulong) * zend_bitset_len(_len)); \
85
+ } while (0)
86
+
87
+ #define ZEND_WORKLIST_FREE_ALLOCA(w, use_heap) \
88
+ free_alloca((w)->stack.buf, use_heap)
89
+
90
+ static inline int zend_worklist_prepare(zend_arena **arena, zend_worklist *worklist, int len)
91
+ {
92
+ ZEND_ASSERT(len >= 0);
93
+ worklist->visited = (zend_bitset)zend_arena_calloc(arena, sizeof(zend_ulong), zend_bitset_len(len));
94
+ if (!worklist->visited) {
95
+ return FAILURE;
96
+ }
97
+ return zend_worklist_stack_prepare(arena, &worklist->stack, len);
98
+ }
99
+
100
+ static inline int zend_worklist_len(zend_worklist *worklist)
101
+ {
102
+ return worklist->stack.len;
103
+ }
104
+
105
+ static inline int zend_worklist_push(zend_worklist *worklist, int i)
106
+ {
107
+ ZEND_ASSERT(i >= 0 && i < worklist->stack.capacity);
108
+
109
+ if (zend_bitset_in(worklist->visited, i)) {
110
+ return 0;
111
+ }
112
+
113
+ zend_bitset_incl(worklist->visited, i);
114
+ zend_worklist_stack_push(&worklist->stack, i);
115
+ return 1;
116
+ }
117
+
118
+ static inline int zend_worklist_peek(zend_worklist *worklist)
119
+ {
120
+ return zend_worklist_stack_peek(&worklist->stack);
121
+ }
122
+
123
+ static inline int zend_worklist_pop(zend_worklist *worklist)
124
+ {
125
+ /* Does not clear visited flag */
126
+ return zend_worklist_stack_pop(&worklist->stack);
127
+ }
128
+
129
+ #endif /* _ZEND_WORKLIST_H_ */
130
+
131
+ /*
132
+ * Local variables:
133
+ * tab-width: 4
134
+ * c-basic-offset: 4
135
+ * indent-tabs-mode: t
136
+ * End:
137
+ */
pcov-1.0.11/pcov-1.0.11/cfg/702/zend_cfg.c ADDED
@@ -0,0 +1,642 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ +----------------------------------------------------------------------+
3
+ | Zend Engine, CFG - Control Flow Graph |
4
+ +----------------------------------------------------------------------+
5
+ | Copyright (c) 1998-2018 The PHP Group |
6
+ +----------------------------------------------------------------------+
7
+ | This source file is subject to version 3.01 of the PHP license, |
8
+ | that is bundled with this package in the file LICENSE, and is |
9
+ | available through the world-wide-web at the following url: |
10
+ | http://www.php.net/license/3_01.txt |
11
+ | If you did not receive a copy of the PHP license and are unable to |
12
+ | obtain it through the world-wide-web, please send a note to |
13
+ | [email protected] so we can mail you a copy immediately. |
14
+ +----------------------------------------------------------------------+
15
+ | Authors: Dmitry Stogov <[email protected]> |
16
+ +----------------------------------------------------------------------+
17
+
18
+ This source file has been adapted for pcov so that the CFG from O+ is standalone
19
+ */
20
+
21
+ #include "php.h"
22
+ #include "zend_compile.h"
23
+ #include "zend_cfg.h"
24
+ #include "zend_worklist.h"
25
+
26
+ /* func flags */
27
+ #define ZEND_FUNC_INDIRECT_VAR_ACCESS (1<<0) /* accesses variables by name */
28
+ #define ZEND_FUNC_HAS_CALLS (1<<1)
29
+ #define ZEND_FUNC_VARARG (1<<2) /* uses func_get_args() */
30
+ #define ZEND_FUNC_NO_LOOPS (1<<3)
31
+ #define ZEND_FUNC_IRREDUCIBLE (1<<4)
32
+ #define ZEND_FUNC_RECURSIVE (1<<7)
33
+ #define ZEND_FUNC_RECURSIVE_DIRECTLY (1<<8)
34
+ #define ZEND_FUNC_RECURSIVE_INDIRECTLY (1<<9)
35
+ #define ZEND_FUNC_HAS_EXTENDED_INFO (1<<10)
36
+
37
+ /* The following flags are valid only for return values of internal functions
38
+ * returned by zend_get_func_info()
39
+ */
40
+ #define FUNC_MAY_WARN (1<<30)
41
+
42
+ typedef struct _zend_func_info zend_func_info;
43
+ typedef struct _zend_call_info zend_call_info;
44
+
45
+ #define ZEND_FUNC_INFO(op_array) \
46
+ ((zend_func_info*)((op_array)->reserved[zend_func_info_rid]))
47
+
48
+ #define ZEND_SET_FUNC_INFO(op_array, info) do { \
49
+ zend_func_info** pinfo = (zend_func_info**)&(op_array)->reserved[zend_func_info_rid]; \
50
+ *pinfo = info; \
51
+ } while (0)
52
+
53
+ static uint32_t zend_cfg_classify_function(zend_string *name, uint32_t num_args) {
54
+ if (zend_string_equals_literal(name, "extract")) {
55
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
56
+ } else if (zend_string_equals_literal(name, "compact")) {
57
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
58
+ } else if (zend_string_equals_literal(name, "parse_str") && num_args <= 1) {
59
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
60
+ } else if (zend_string_equals_literal(name, "mb_parse_str") && num_args <= 1) {
61
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
62
+ } else if (zend_string_equals_literal(name, "get_defined_vars")) {
63
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
64
+ } else if (zend_string_equals_literal(name, "assert")) {
65
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
66
+ } else if (zend_string_equals_literal(name, "func_num_args")) {
67
+ return ZEND_FUNC_VARARG;
68
+ } else if (zend_string_equals_literal(name, "func_get_arg")) {
69
+ return ZEND_FUNC_VARARG;
70
+ } else if (zend_string_equals_literal(name, "func_get_args")) {
71
+ return ZEND_FUNC_VARARG;
72
+ } else {
73
+ return 0;
74
+ }
75
+ }
76
+
77
+ static void zend_mark_reachable(zend_op *opcodes, zend_cfg *cfg, zend_basic_block *b) /* {{{ */
78
+ {
79
+ zend_basic_block *blocks = cfg->blocks;
80
+
81
+ while (1) {
82
+ int i;
83
+
84
+ b->flags |= ZEND_BB_REACHABLE;
85
+ if (b->successors_count == 0) {
86
+ b->flags |= ZEND_BB_EXIT;
87
+ return;
88
+ }
89
+
90
+ for (i = 0; i < b->successors_count; i++) {
91
+ zend_basic_block *succ = blocks + b->successors[i];
92
+
93
+ if (b->len != 0) {
94
+ zend_uchar opcode = opcodes[b->start + b->len - 1].opcode;
95
+ if (b->successors_count == 1) {
96
+ if (opcode == ZEND_JMP) {
97
+ succ->flags |= ZEND_BB_TARGET;
98
+ } else {
99
+ succ->flags |= ZEND_BB_FOLLOW;
100
+
101
+ if (cfg->split_at_calls) {
102
+ if (opcode == ZEND_INCLUDE_OR_EVAL ||
103
+ opcode == ZEND_GENERATOR_CREATE ||
104
+ opcode == ZEND_YIELD ||
105
+ opcode == ZEND_YIELD_FROM ||
106
+ opcode == ZEND_DO_FCALL ||
107
+ opcode == ZEND_DO_UCALL ||
108
+ opcode == ZEND_DO_FCALL_BY_NAME) {
109
+ succ->flags |= ZEND_BB_ENTRY;
110
+ }
111
+ }
112
+ if (cfg->split_at_recv) {
113
+ if (opcode == ZEND_RECV ||
114
+ opcode == ZEND_RECV_INIT) {
115
+ succ->flags |= ZEND_BB_RECV_ENTRY;
116
+ }
117
+ }
118
+ }
119
+ } else if (b->successors_count == 2) {
120
+ if (i == 0 || opcode == ZEND_JMPZNZ) {
121
+ succ->flags |= ZEND_BB_TARGET;
122
+ } else {
123
+ succ->flags |= ZEND_BB_FOLLOW;
124
+ }
125
+ } else {
126
+ ZEND_ASSERT(opcode == ZEND_SWITCH_LONG || opcode == ZEND_SWITCH_STRING);
127
+ if (i == b->successors_count - 1) {
128
+ succ->flags |= ZEND_BB_FOLLOW | ZEND_BB_TARGET;
129
+ } else {
130
+ succ->flags |= ZEND_BB_TARGET;
131
+ }
132
+ }
133
+ } else {
134
+ succ->flags |= ZEND_BB_FOLLOW;
135
+ }
136
+
137
+ if (i == b->successors_count - 1) {
138
+ /* Tail call optimization */
139
+ if (succ->flags & ZEND_BB_REACHABLE) {
140
+ return;
141
+ }
142
+
143
+ b = succ;
144
+ break;
145
+ } else {
146
+ /* Recusively check reachability */
147
+ if (!(succ->flags & ZEND_BB_REACHABLE)) {
148
+ zend_mark_reachable(opcodes, cfg, succ);
149
+ }
150
+ }
151
+ }
152
+ }
153
+ }
154
+ /* }}} */
155
+
156
+ static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg, int start) /* {{{ */
157
+ {
158
+ zend_basic_block *blocks = cfg->blocks;
159
+
160
+ blocks[start].flags = ZEND_BB_START;
161
+ zend_mark_reachable(op_array->opcodes, cfg, blocks + start);
162
+
163
+ if (op_array->last_live_range || op_array->last_try_catch) {
164
+ zend_basic_block *b;
165
+ int j, changed;
166
+ uint32_t *block_map = cfg->map;
167
+
168
+ do {
169
+ changed = 0;
170
+
171
+ /* Add live range paths */
172
+ for (j = 0; j < op_array->last_live_range; j++) {
173
+ zend_live_range *live_range = &op_array->live_range[j];
174
+ if (live_range->var == (uint32_t)-1) {
175
+ /* this live range already removed */
176
+ continue;
177
+ }
178
+ b = blocks + block_map[live_range->start];
179
+ if (b->flags & ZEND_BB_REACHABLE) {
180
+ while (b->len > 0 && op_array->opcodes[b->start].opcode == ZEND_NOP) {
181
+ /* check if NOP breaks incorrect smart branch */
182
+ if (b->len == 2
183
+ && (op_array->opcodes[b->start + 1].opcode == ZEND_JMPZ
184
+ || op_array->opcodes[b->start + 1].opcode == ZEND_JMPNZ)
185
+ && (op_array->opcodes[b->start + 1].op1_type & (IS_CV|IS_CONST))
186
+ && b->start > 0
187
+ && zend_is_smart_branch(op_array->opcodes + b->start - 1)) {
188
+ break;
189
+ }
190
+ b->start++;
191
+ b->len--;
192
+ }
193
+ if (b->len == 0 && (uint32_t)b->successors[0] == block_map[live_range->end]) {
194
+ /* mark as removed (empty live range) */
195
+ live_range->var = (uint32_t)-1;
196
+ continue;
197
+ }
198
+ b->flags |= ZEND_BB_GEN_VAR;
199
+ b = blocks + block_map[live_range->end];
200
+ b->flags |= ZEND_BB_KILL_VAR;
201
+ if (!(b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE))) {
202
+ if (cfg->split_at_live_ranges) {
203
+ changed = 1;
204
+ zend_mark_reachable(op_array->opcodes, cfg, b);
205
+ } else {
206
+ b->flags |= ZEND_BB_UNREACHABLE_FREE;
207
+ }
208
+ }
209
+ } else {
210
+ ZEND_ASSERT(!(blocks[block_map[live_range->end]].flags & ZEND_BB_REACHABLE));
211
+ }
212
+ }
213
+
214
+ /* Add exception paths */
215
+ for (j = 0; j < op_array->last_try_catch; j++) {
216
+
217
+ /* check for jumps into the middle of try block */
218
+ b = blocks + block_map[op_array->try_catch_array[j].try_op];
219
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
220
+ zend_basic_block *end;
221
+
222
+ if (op_array->try_catch_array[j].catch_op) {
223
+ end = blocks + block_map[op_array->try_catch_array[j].catch_op];
224
+ while (b != end) {
225
+ if (b->flags & ZEND_BB_REACHABLE) {
226
+ op_array->try_catch_array[j].try_op = b->start;
227
+ break;
228
+ }
229
+ b++;
230
+ }
231
+ }
232
+ b = blocks + block_map[op_array->try_catch_array[j].try_op];
233
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
234
+ if (op_array->try_catch_array[j].finally_op) {
235
+ end = blocks + block_map[op_array->try_catch_array[j].finally_op];
236
+ while (b != end) {
237
+ if (b->flags & ZEND_BB_REACHABLE) {
238
+ op_array->try_catch_array[j].try_op = op_array->try_catch_array[j].catch_op;
239
+ changed = 1;
240
+ zend_mark_reachable(op_array->opcodes, cfg, blocks + block_map[op_array->try_catch_array[j].try_op]);
241
+ break;
242
+ }
243
+ b++;
244
+ }
245
+ }
246
+ }
247
+ }
248
+
249
+ b = blocks + block_map[op_array->try_catch_array[j].try_op];
250
+ if (b->flags & ZEND_BB_REACHABLE) {
251
+ b->flags |= ZEND_BB_TRY;
252
+ if (op_array->try_catch_array[j].catch_op) {
253
+ b = blocks + block_map[op_array->try_catch_array[j].catch_op];
254
+ b->flags |= ZEND_BB_CATCH;
255
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
256
+ changed = 1;
257
+ zend_mark_reachable(op_array->opcodes, cfg, b);
258
+ }
259
+ }
260
+ if (op_array->try_catch_array[j].finally_op) {
261
+ b = blocks + block_map[op_array->try_catch_array[j].finally_op];
262
+ b->flags |= ZEND_BB_FINALLY;
263
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
264
+ changed = 1;
265
+ zend_mark_reachable(op_array->opcodes, cfg, b);
266
+ }
267
+ }
268
+ if (op_array->try_catch_array[j].finally_end) {
269
+ b = blocks + block_map[op_array->try_catch_array[j].finally_end];
270
+ b->flags |= ZEND_BB_FINALLY_END;
271
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
272
+ changed = 1;
273
+ zend_mark_reachable(op_array->opcodes, cfg, b);
274
+ }
275
+ }
276
+ } else {
277
+ if (op_array->try_catch_array[j].catch_op) {
278
+ ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].catch_op]].flags & ZEND_BB_REACHABLE));
279
+ }
280
+ if (op_array->try_catch_array[j].finally_op) {
281
+ ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_op]].flags & ZEND_BB_REACHABLE));
282
+ }
283
+ if (op_array->try_catch_array[j].finally_end) {
284
+ ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_end]].flags & ZEND_BB_REACHABLE));
285
+ }
286
+ }
287
+ }
288
+ } while (changed);
289
+ }
290
+ }
291
+ /* }}} */
292
+
293
+ static void initialize_block(zend_basic_block *block) {
294
+ block->flags = 0;
295
+ block->successors = block->successors_storage;
296
+ block->successors_count = 0;
297
+ block->predecessors_count = 0;
298
+ block->predecessor_offset = -1;
299
+ block->idom = -1;
300
+ block->loop_header = -1;
301
+ block->level = -1;
302
+ block->children = -1;
303
+ block->next_child = -1;
304
+ }
305
+
306
+ #define BB_START(i) do { \
307
+ if (!block_map[i]) { blocks_count++;} \
308
+ block_map[i]++; \
309
+ } while (0)
310
+
311
+ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg) /* {{{ */
312
+ {
313
+ uint32_t flags = 0;
314
+ uint32_t i;
315
+ int j;
316
+ uint32_t *block_map;
317
+ zend_function *fn;
318
+ int blocks_count = 0;
319
+ zend_basic_block *blocks;
320
+ zval *zv;
321
+ zend_bool extra_entry_block = 0;
322
+
323
+ cfg->split_at_live_ranges = (build_flags & ZEND_CFG_SPLIT_AT_LIVE_RANGES) != 0;
324
+ cfg->split_at_calls = (build_flags & ZEND_CFG_STACKLESS) != 0;
325
+ cfg->split_at_recv = (build_flags & ZEND_CFG_RECV_ENTRY) != 0 && (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0;
326
+
327
+ cfg->map = block_map = zend_arena_calloc(arena, op_array->last, sizeof(uint32_t));
328
+
329
+ /* Build CFG, Step 1: Find basic blocks starts, calculate number of blocks */
330
+ BB_START(0);
331
+ for (i = 0; i < op_array->last; i++) {
332
+ zend_op *opline = op_array->opcodes + i;
333
+ switch(opline->opcode) {
334
+ case ZEND_RECV:
335
+ case ZEND_RECV_INIT:
336
+ if (build_flags & ZEND_CFG_RECV_ENTRY) {
337
+ BB_START(i + 1);
338
+ }
339
+ break;
340
+ case ZEND_RETURN:
341
+ case ZEND_RETURN_BY_REF:
342
+ case ZEND_GENERATOR_RETURN:
343
+ case ZEND_EXIT:
344
+ case ZEND_THROW:
345
+ if (i + 1 < op_array->last) {
346
+ BB_START(i + 1);
347
+ }
348
+ break;
349
+ case ZEND_INCLUDE_OR_EVAL:
350
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
351
+ case ZEND_GENERATOR_CREATE:
352
+ case ZEND_YIELD:
353
+ case ZEND_YIELD_FROM:
354
+ if (build_flags & ZEND_CFG_STACKLESS) {
355
+ BB_START(i + 1);
356
+ }
357
+ break;
358
+ case ZEND_DO_FCALL:
359
+ case ZEND_DO_UCALL:
360
+ case ZEND_DO_FCALL_BY_NAME:
361
+ flags |= ZEND_FUNC_HAS_CALLS;
362
+ if (build_flags & ZEND_CFG_STACKLESS) {
363
+ BB_START(i + 1);
364
+ }
365
+ break;
366
+ case ZEND_DO_ICALL:
367
+ flags |= ZEND_FUNC_HAS_CALLS;
368
+ break;
369
+ case ZEND_INIT_FCALL:
370
+ case ZEND_INIT_NS_FCALL_BY_NAME:
371
+ zv = CRT_CONSTANT(opline->op2);
372
+ if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
373
+ /* The third literal is the lowercased unqualified name */
374
+ zv += 2;
375
+ }
376
+ if ((fn = zend_hash_find_ptr(EG(function_table), Z_STR_P(zv))) != NULL) {
377
+ if (fn->type == ZEND_INTERNAL_FUNCTION) {
378
+ flags |= zend_cfg_classify_function(
379
+ Z_STR_P(zv), opline->extended_value);
380
+ }
381
+ }
382
+ break;
383
+ case ZEND_FAST_CALL:
384
+ BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
385
+ BB_START(i + 1);
386
+ break;
387
+ case ZEND_FAST_RET:
388
+ if (i + 1 < op_array->last) {
389
+ BB_START(i + 1);
390
+ }
391
+ break;
392
+ case ZEND_JMP:
393
+ BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
394
+ if (i + 1 < op_array->last) {
395
+ BB_START(i + 1);
396
+ }
397
+ break;
398
+ case ZEND_JMPZNZ:
399
+ BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
400
+ BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
401
+ if (i + 1 < op_array->last) {
402
+ BB_START(i + 1);
403
+ }
404
+ break;
405
+ case ZEND_JMPZ:
406
+ case ZEND_JMPNZ:
407
+ case ZEND_JMPZ_EX:
408
+ case ZEND_JMPNZ_EX:
409
+ case ZEND_JMP_SET:
410
+ case ZEND_COALESCE:
411
+ case ZEND_ASSERT_CHECK:
412
+ BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
413
+ BB_START(i + 1);
414
+ break;
415
+ case ZEND_CATCH:
416
+ if (!opline->result.num) {
417
+ BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
418
+ }
419
+ BB_START(i + 1);
420
+ break;
421
+ case ZEND_DECLARE_ANON_CLASS:
422
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
423
+ case ZEND_FE_FETCH_R:
424
+ case ZEND_FE_FETCH_RW:
425
+ BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
426
+ BB_START(i + 1);
427
+ break;
428
+ case ZEND_FE_RESET_R:
429
+ case ZEND_FE_RESET_RW:
430
+ BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
431
+ BB_START(i + 1);
432
+ break;
433
+ case ZEND_SWITCH_LONG:
434
+ case ZEND_SWITCH_STRING:
435
+ {
436
+ HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2));
437
+ zval *zv;
438
+ ZEND_HASH_FOREACH_VAL(jumptable, zv) {
439
+ BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)));
440
+ } ZEND_HASH_FOREACH_END();
441
+ BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
442
+ BB_START(i + 1);
443
+ break;
444
+ }
445
+ case ZEND_UNSET_VAR:
446
+ case ZEND_ISSET_ISEMPTY_VAR:
447
+ if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_LOCAL) {
448
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
449
+ } else if (((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL ||
450
+ (opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL_LOCK) &&
451
+ !op_array->function_name) {
452
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
453
+ }
454
+ break;
455
+ case ZEND_FETCH_R:
456
+ case ZEND_FETCH_W:
457
+ case ZEND_FETCH_RW:
458
+ case ZEND_FETCH_FUNC_ARG:
459
+ case ZEND_FETCH_IS:
460
+ case ZEND_FETCH_UNSET:
461
+ if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_LOCAL) {
462
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
463
+ } else if (((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL ||
464
+ (opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL_LOCK) &&
465
+ !op_array->function_name) {
466
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
467
+ }
468
+ break;
469
+ case ZEND_FUNC_GET_ARGS:
470
+ flags |= ZEND_FUNC_VARARG;
471
+ break;
472
+ }
473
+ }
474
+
475
+ /* If the entry block has predecessors, we may need to split it */
476
+ if ((build_flags & ZEND_CFG_NO_ENTRY_PREDECESSORS)
477
+ && op_array->last > 0 && block_map[0] > 1) {
478
+ extra_entry_block = 1;
479
+ }
480
+
481
+ if (cfg->split_at_live_ranges) {
482
+ for (j = 0; j < op_array->last_live_range; j++) {
483
+ BB_START(op_array->live_range[j].start);
484
+ BB_START(op_array->live_range[j].end);
485
+ }
486
+ }
487
+
488
+ if (op_array->last_try_catch) {
489
+ for (j = 0; j < op_array->last_try_catch; j++) {
490
+ BB_START(op_array->try_catch_array[j].try_op);
491
+ if (op_array->try_catch_array[j].catch_op) {
492
+ BB_START(op_array->try_catch_array[j].catch_op);
493
+ }
494
+ if (op_array->try_catch_array[j].finally_op) {
495
+ BB_START(op_array->try_catch_array[j].finally_op);
496
+ }
497
+ if (op_array->try_catch_array[j].finally_end) {
498
+ BB_START(op_array->try_catch_array[j].finally_end);
499
+ }
500
+ }
501
+ }
502
+
503
+ blocks_count += extra_entry_block;
504
+ cfg->blocks_count = blocks_count;
505
+
506
+ /* Build CFG, Step 2: Build Array of Basic Blocks */
507
+ cfg->blocks = blocks = zend_arena_calloc(arena, sizeof(zend_basic_block), blocks_count);
508
+
509
+ blocks_count = -1;
510
+
511
+ if (extra_entry_block) {
512
+ initialize_block(&blocks[0]);
513
+ blocks[0].start = 0;
514
+ blocks[0].len = 0;
515
+ blocks_count++;
516
+ }
517
+
518
+ for (i = 0; i < op_array->last; i++) {
519
+ if (block_map[i]) {
520
+ if (blocks_count >= 0) {
521
+ blocks[blocks_count].len = i - blocks[blocks_count].start;
522
+ }
523
+ blocks_count++;
524
+ initialize_block(&blocks[blocks_count]);
525
+ blocks[blocks_count].start = i;
526
+ }
527
+ block_map[i] = blocks_count;
528
+ }
529
+
530
+ blocks[blocks_count].len = i - blocks[blocks_count].start;
531
+ blocks_count++;
532
+
533
+ /* Build CFG, Step 3: Calculate successors */
534
+ for (j = 0; j < blocks_count; j++) {
535
+ zend_basic_block *block = &blocks[j];
536
+ zend_op *opline;
537
+ if (block->len == 0) {
538
+ block->successors_count = 1;
539
+ block->successors[0] = j + 1;
540
+ continue;
541
+ }
542
+
543
+ opline = op_array->opcodes + block->start + block->len - 1;
544
+ switch (opline->opcode) {
545
+ case ZEND_FAST_RET:
546
+ case ZEND_RETURN:
547
+ case ZEND_RETURN_BY_REF:
548
+ case ZEND_GENERATOR_RETURN:
549
+ case ZEND_EXIT:
550
+ case ZEND_THROW:
551
+ break;
552
+ case ZEND_JMP:
553
+ block->successors_count = 1;
554
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes];
555
+ break;
556
+ case ZEND_JMPZNZ:
557
+ block->successors_count = 2;
558
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
559
+ block->successors[1] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
560
+ break;
561
+ case ZEND_JMPZ:
562
+ case ZEND_JMPNZ:
563
+ case ZEND_JMPZ_EX:
564
+ case ZEND_JMPNZ_EX:
565
+ case ZEND_JMP_SET:
566
+ case ZEND_COALESCE:
567
+ case ZEND_ASSERT_CHECK:
568
+ block->successors_count = 2;
569
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
570
+ block->successors[1] = j + 1;
571
+ break;
572
+ case ZEND_CATCH:
573
+ if (!opline->result.num) {
574
+ block->successors_count = 2;
575
+ block->successors[0] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
576
+ block->successors[1] = j + 1;
577
+ } else {
578
+ block->successors_count = 1;
579
+ block->successors[0] = j + 1;
580
+ }
581
+ break;
582
+ case ZEND_DECLARE_ANON_CLASS:
583
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
584
+ case ZEND_FE_FETCH_R:
585
+ case ZEND_FE_FETCH_RW:
586
+ block->successors_count = 2;
587
+ block->successors[0] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
588
+ block->successors[1] = j + 1;
589
+ break;
590
+ case ZEND_FE_RESET_R:
591
+ case ZEND_FE_RESET_RW:
592
+ block->successors_count = 2;
593
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
594
+ block->successors[1] = j + 1;
595
+ break;
596
+ case ZEND_FAST_CALL:
597
+ block->successors_count = 2;
598
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes];
599
+ block->successors[1] = j + 1;
600
+ break;
601
+ case ZEND_SWITCH_LONG:
602
+ case ZEND_SWITCH_STRING:
603
+ {
604
+ HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2));
605
+ zval *zv;
606
+ uint32_t s = 0;
607
+
608
+ block->successors_count = 2 + zend_hash_num_elements(jumptable);
609
+ block->successors = zend_arena_calloc(arena, block->successors_count, sizeof(int));
610
+
611
+ ZEND_HASH_FOREACH_VAL(jumptable, zv) {
612
+ block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))];
613
+ } ZEND_HASH_FOREACH_END();
614
+
615
+ block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
616
+ block->successors[s++] = j + 1;
617
+ break;
618
+ }
619
+ default:
620
+ block->successors_count = 1;
621
+ block->successors[0] = j + 1;
622
+ break;
623
+ }
624
+ }
625
+
626
+ /* Build CFG, Step 4, Mark Reachable Basic Blocks */
627
+ zend_mark_reachable_blocks(op_array, cfg, 0);
628
+
629
+ cfg->dynamic = (flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) != 0;
630
+ cfg->vararg = (flags & ZEND_FUNC_VARARG) != 0;
631
+
632
+ return SUCCESS;
633
+ }
634
+ /* }}} */
635
+
636
+ /*
637
+ * Local variables:
638
+ * tab-width: 4
639
+ * c-basic-offset: 4
640
+ * indent-tabs-mode: t
641
+ * End:
642
+ */
pcov-1.0.11/pcov-1.0.11/cfg/702/zend_cfg.h ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ +----------------------------------------------------------------------+
3
+ | Zend Engine, CFG - Control Flow Graph |
4
+ +----------------------------------------------------------------------+
5
+ | Copyright (c) 1998-2018 The PHP Group |
6
+ +----------------------------------------------------------------------+
7
+ | This source file is subject to version 3.01 of the PHP license, |
8
+ | that is bundled with this package in the file LICENSE, and is |
9
+ | available through the world-wide-web at the following url: |
10
+ | http://www.php.net/license/3_01.txt |
11
+ | If you did not receive a copy of the PHP license and are unable to |
12
+ | obtain it through the world-wide-web, please send a note to |
13
+ | [email protected] so we can mail you a copy immediately. |
14
+ +----------------------------------------------------------------------+
15
+ | Authors: Dmitry Stogov <[email protected]> |
16
+ +----------------------------------------------------------------------+
17
+ */
18
+
19
+ #ifndef ZEND_CFG_H
20
+ #define ZEND_CFG_H
21
+
22
+ /* zend_basic_bloc.flags */
23
+ #define ZEND_BB_START (1<<0) /* fist block */
24
+ #define ZEND_BB_FOLLOW (1<<1) /* follows the next block */
25
+ #define ZEND_BB_TARGET (1<<2) /* jump taget */
26
+ #define ZEND_BB_EXIT (1<<3) /* without successors */
27
+ #define ZEND_BB_ENTRY (1<<4) /* stackless entry */
28
+ #define ZEND_BB_TRY (1<<5) /* start of try block */
29
+ #define ZEND_BB_CATCH (1<<6) /* start of catch block */
30
+ #define ZEND_BB_FINALLY (1<<7) /* start of finally block */
31
+ #define ZEND_BB_FINALLY_END (1<<8) /* end of finally block */
32
+ #define ZEND_BB_GEN_VAR (1<<9) /* start of live range */
33
+ #define ZEND_BB_KILL_VAR (1<<10) /* end of live range */
34
+ #define ZEND_BB_UNREACHABLE_FREE (1<<11) /* unreachable loop free */
35
+ #define ZEND_BB_RECV_ENTRY (1<<12) /* RECV entry */
36
+
37
+ #define ZEND_BB_LOOP_HEADER (1<<16)
38
+ #define ZEND_BB_IRREDUCIBLE_LOOP (1<<17)
39
+
40
+ #define ZEND_BB_REACHABLE (1<<31)
41
+
42
+ #define ZEND_BB_PROTECTED (ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY|ZEND_BB_TRY|ZEND_BB_CATCH|ZEND_BB_FINALLY|ZEND_BB_FINALLY_END|ZEND_BB_GEN_VAR|ZEND_BB_KILL_VAR)
43
+
44
+ typedef struct _zend_basic_block {
45
+ int *successors; /* successor block indices */
46
+ uint32_t flags;
47
+ uint32_t start; /* first opcode number */
48
+ uint32_t len; /* number of opcodes */
49
+ int successors_count; /* number of successors */
50
+ int predecessors_count; /* number of predecessors */
51
+ int predecessor_offset; /* offset of 1-st predecessor */
52
+ int idom; /* immediate dominator block */
53
+ int loop_header; /* closest loop header, or -1 */
54
+ int level; /* steps away from the entry in the dom. tree */
55
+ int children; /* list of dominated blocks */
56
+ int next_child; /* next dominated block */
57
+ int successors_storage[2]; /* up to 2 successor blocks */
58
+ } zend_basic_block;
59
+
60
+ /*
61
+ +------------+---+---+---+---+---+
62
+ | |OP1|OP2|EXT| 0 | 1 |
63
+ +------------+---+---+---+---+---+
64
+ |JMP |ADR| | |OP1| - |
65
+ |JMPZ | |ADR| |OP2|FOL|
66
+ |JMPNZ | |ADR| |OP2|FOL|
67
+ |JMPZNZ | |ADR|ADR|OP2|EXT|
68
+ |JMPZ_EX | |ADR| |OP2|FOL|
69
+ |JMPNZ_EX | |ADR| |OP2|FOL|
70
+ |JMP_SET | |ADR| |OP2|FOL|
71
+ |COALESCE | |ADR| |OP2|FOL|
72
+ |ASSERT_CHK | |ADR| |OP2|FOL|
73
+ |NEW | |ADR| |OP2|FOL|
74
+ |DCL_ANON* |ADR| | |OP1|FOL|
75
+ |FE_RESET_* | |ADR| |OP2|FOL|
76
+ |FE_FETCH_* | | |ADR|EXT|FOL|
77
+ |CATCH | | |ADR|EXT|FOL|
78
+ |FAST_CALL |ADR| | |OP1|FOL|
79
+ |FAST_RET | | | | - | - |
80
+ |RETURN* | | | | - | - |
81
+ |EXIT | | | | - | - |
82
+ |THROW | | | | - | - |
83
+ |* | | | |FOL| - |
84
+ +------------+---+---+---+---+---+
85
+ */
86
+
87
+ typedef struct _zend_cfg {
88
+ int blocks_count; /* number of basic blocks */
89
+ int edges_count; /* number of edges */
90
+ zend_basic_block *blocks; /* array of basic blocks */
91
+ int *predecessors;
92
+ uint32_t *map;
93
+ unsigned int split_at_live_ranges : 1;
94
+ unsigned int split_at_calls : 1;
95
+ unsigned int split_at_recv : 1;
96
+ unsigned int dynamic : 1; /* accesses varables by name */
97
+ unsigned int vararg : 1; /* uses func_get_args() */
98
+ } zend_cfg;
99
+
100
+ /* Build Flags */
101
+ #define ZEND_RT_CONSTANTS (1<<31)
102
+ #define ZEND_CFG_STACKLESS (1<<30)
103
+ #define ZEND_SSA_DEBUG_LIVENESS (1<<29)
104
+ #define ZEND_SSA_DEBUG_PHI_PLACEMENT (1<<28)
105
+ #define ZEND_SSA_RC_INFERENCE (1<<27)
106
+ #define ZEND_CFG_SPLIT_AT_LIVE_RANGES (1<<26)
107
+ #define ZEND_CFG_NO_ENTRY_PREDECESSORS (1<<25)
108
+ #define ZEND_CFG_RECV_ENTRY (1<<24)
109
+ #define ZEND_CALL_TREE (1<<23)
110
+ #define ZEND_SSA_USE_CV_RESULTS (1<<22)
111
+
112
+ #define CRT_CONSTANT_EX(op_array, node, rt_constants) \
113
+ ((rt_constants) ? \
114
+ RT_CONSTANT(op_array, (node)) \
115
+ : \
116
+ CT_CONSTANT_EX(op_array, (node).constant) \
117
+ )
118
+
119
+ #define CRT_CONSTANT(node) \
120
+ CRT_CONSTANT_EX(op_array, node, (build_flags & ZEND_RT_CONSTANTS))
121
+
122
+ #define RETURN_VALUE_USED(opline) \
123
+ ((opline)->result_type != IS_UNUSED)
124
+
125
+ BEGIN_EXTERN_C()
126
+
127
+ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg);
128
+
129
+ END_EXTERN_C()
130
+
131
+ #endif /* ZEND_CFG_H */
132
+
133
+ /*
134
+ * Local variables:
135
+ * tab-width: 4
136
+ * c-basic-offset: 4
137
+ * indent-tabs-mode: t
138
+ * End:
139
+ */
pcov-1.0.11/pcov-1.0.11/cfg/702/zend_worklist.h ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ +----------------------------------------------------------------------+
3
+ | Zend Engine |
4
+ +----------------------------------------------------------------------+
5
+ | Copyright (c) 1998-2018 The PHP Group |
6
+ +----------------------------------------------------------------------+
7
+ | This source file is subject to version 3.01 of the PHP license, |
8
+ | that is bundled with this package in the file LICENSE, and is |
9
+ | available through the world-wide-web at the following url: |
10
+ | http://www.php.net/license/3_01.txt |
11
+ | If you did not receive a copy of the PHP license and are unable to |
12
+ | obtain it through the world-wide-web, please send a note to |
13
+ | [email protected] so we can mail you a copy immediately. |
14
+ +----------------------------------------------------------------------+
15
+ | Authors: Andy Wingo <[email protected]> |
16
+ +----------------------------------------------------------------------+
17
+ */
18
+
19
+ /* $Id:$ */
20
+
21
+ #ifndef _ZEND_WORKLIST_H_
22
+ #define _ZEND_WORKLIST_H_
23
+
24
+ #include "zend_arena.h"
25
+ #include "zend_bitset.h"
26
+
27
+ typedef struct _zend_worklist_stack {
28
+ int *buf;
29
+ int len;
30
+ int capacity;
31
+ } zend_worklist_stack;
32
+
33
+ #define ZEND_WORKLIST_STACK_ALLOCA(s, _len, use_heap) do { \
34
+ (s)->buf = (int*)do_alloca(sizeof(int) * _len, use_heap); \
35
+ (s)->len = 0; \
36
+ (s)->capacity = _len; \
37
+ } while (0)
38
+
39
+ #define ZEND_WORKLIST_STACK_FREE_ALLOCA(s, use_heap) \
40
+ free_alloca((s)->buf, use_heap)
41
+
42
+ static inline int zend_worklist_stack_prepare(zend_arena **arena, zend_worklist_stack *stack, int len)
43
+ {
44
+ ZEND_ASSERT(len >= 0);
45
+
46
+ stack->buf = (int*)zend_arena_calloc(arena, sizeof(*stack->buf), len);
47
+ stack->len = 0;
48
+ stack->capacity = len;
49
+
50
+ return SUCCESS;
51
+ }
52
+
53
+ static inline void zend_worklist_stack_push(zend_worklist_stack *stack, int i)
54
+ {
55
+ ZEND_ASSERT(stack->len < stack->capacity);
56
+ stack->buf[stack->len++] = i;
57
+ }
58
+
59
+ static inline int zend_worklist_stack_peek(zend_worklist_stack *stack)
60
+ {
61
+ ZEND_ASSERT(stack->len);
62
+ return stack->buf[stack->len - 1];
63
+ }
64
+
65
+ static inline int zend_worklist_stack_pop(zend_worklist_stack *stack)
66
+ {
67
+ ZEND_ASSERT(stack->len);
68
+ return stack->buf[--stack->len];
69
+ }
70
+
71
+ typedef struct _zend_worklist {
72
+ zend_bitset visited;
73
+ zend_worklist_stack stack;
74
+ } zend_worklist;
75
+
76
+ #define ZEND_WORKLIST_ALLOCA(w, _len, use_heap) do { \
77
+ (w)->stack.buf = (int*)do_alloca(ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len) + sizeof(zend_ulong) * zend_bitset_len(_len), use_heap); \
78
+ (w)->stack.len = 0; \
79
+ (w)->stack.capacity = _len; \
80
+ (w)->visited = (zend_bitset)((char*)(w)->stack.buf + ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len)); \
81
+ memset((w)->visited, 0, sizeof(zend_ulong) * zend_bitset_len(_len)); \
82
+ } while (0)
83
+
84
+ #define ZEND_WORKLIST_FREE_ALLOCA(w, use_heap) \
85
+ free_alloca((w)->stack.buf, use_heap)
86
+
87
+ static inline int zend_worklist_prepare(zend_arena **arena, zend_worklist *worklist, int len)
88
+ {
89
+ ZEND_ASSERT(len >= 0);
90
+ worklist->visited = (zend_bitset)zend_arena_calloc(arena, sizeof(zend_ulong), zend_bitset_len(len));
91
+ return zend_worklist_stack_prepare(arena, &worklist->stack, len);
92
+ }
93
+
94
+ static inline int zend_worklist_len(zend_worklist *worklist)
95
+ {
96
+ return worklist->stack.len;
97
+ }
98
+
99
+ static inline int zend_worklist_push(zend_worklist *worklist, int i)
100
+ {
101
+ ZEND_ASSERT(i >= 0 && i < worklist->stack.capacity);
102
+
103
+ if (zend_bitset_in(worklist->visited, i)) {
104
+ return 0;
105
+ }
106
+
107
+ zend_bitset_incl(worklist->visited, i);
108
+ zend_worklist_stack_push(&worklist->stack, i);
109
+ return 1;
110
+ }
111
+
112
+ static inline int zend_worklist_peek(zend_worklist *worklist)
113
+ {
114
+ return zend_worklist_stack_peek(&worklist->stack);
115
+ }
116
+
117
+ static inline int zend_worklist_pop(zend_worklist *worklist)
118
+ {
119
+ /* Does not clear visited flag */
120
+ return zend_worklist_stack_pop(&worklist->stack);
121
+ }
122
+
123
+ #endif /* _ZEND_WORKLIST_H_ */
124
+
125
+ /*
126
+ * Local variables:
127
+ * tab-width: 4
128
+ * c-basic-offset: 4
129
+ * indent-tabs-mode: t
130
+ * End:
131
+ */
pcov-1.0.11/pcov-1.0.11/cfg/703/zend_cfg.c ADDED
@@ -0,0 +1,643 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ +----------------------------------------------------------------------+
3
+ | Zend Engine, CFG - Control Flow Graph |
4
+ +----------------------------------------------------------------------+
5
+ | Copyright (c) 1998-2018 The PHP Group |
6
+ +----------------------------------------------------------------------+
7
+ | This source file is subject to version 3.01 of the PHP license, |
8
+ | that is bundled with this package in the file LICENSE, and is |
9
+ | available through the world-wide-web at the following url: |
10
+ | http://www.php.net/license/3_01.txt |
11
+ | If you did not receive a copy of the PHP license and are unable to |
12
+ | obtain it through the world-wide-web, please send a note to |
13
+ | [email protected] so we can mail you a copy immediately. |
14
+ +----------------------------------------------------------------------+
15
+ | Authors: Dmitry Stogov <[email protected]> |
16
+ +----------------------------------------------------------------------+
17
+
18
+ This source file has been adapted for pcov so that the CFG from O+ is standalone
19
+ */
20
+
21
+ #include "php.h"
22
+ #include "zend_compile.h"
23
+ #include "zend_cfg.h"
24
+ #include "zend_worklist.h"
25
+
26
+ /* func flags */
27
+ #define ZEND_FUNC_INDIRECT_VAR_ACCESS (1<<0) /* accesses variables by name */
28
+ #define ZEND_FUNC_HAS_CALLS (1<<1)
29
+ #define ZEND_FUNC_VARARG (1<<2) /* uses func_get_args() */
30
+ #define ZEND_FUNC_NO_LOOPS (1<<3)
31
+ #define ZEND_FUNC_IRREDUCIBLE (1<<4)
32
+ #define ZEND_FUNC_RECURSIVE (1<<7)
33
+ #define ZEND_FUNC_RECURSIVE_DIRECTLY (1<<8)
34
+ #define ZEND_FUNC_RECURSIVE_INDIRECTLY (1<<9)
35
+ #define ZEND_FUNC_HAS_EXTENDED_INFO (1<<10)
36
+
37
+ /* The following flags are valid only for return values of internal functions
38
+ * returned by zend_get_func_info()
39
+ */
40
+ #define FUNC_MAY_WARN (1<<30)
41
+
42
+ typedef struct _zend_func_info zend_func_info;
43
+ typedef struct _zend_call_info zend_call_info;
44
+
45
+ #define ZEND_FUNC_INFO(op_array) \
46
+ ((zend_func_info*)((op_array)->reserved[zend_func_info_rid]))
47
+
48
+ #define ZEND_SET_FUNC_INFO(op_array, info) do { \
49
+ zend_func_info** pinfo = (zend_func_info**)&(op_array)->reserved[zend_func_info_rid]; \
50
+ *pinfo = info; \
51
+ } while (0)
52
+
53
+ static uint32_t zend_cfg_classify_function(zend_string *name, uint32_t num_args) {
54
+ if (zend_string_equals_literal(name, "extract")) {
55
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
56
+ } else if (zend_string_equals_literal(name, "compact")) {
57
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
58
+ } else if (zend_string_equals_literal(name, "parse_str") && num_args <= 1) {
59
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
60
+ } else if (zend_string_equals_literal(name, "mb_parse_str") && num_args <= 1) {
61
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
62
+ } else if (zend_string_equals_literal(name, "get_defined_vars")) {
63
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
64
+ } else if (zend_string_equals_literal(name, "assert")) {
65
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
66
+ } else if (zend_string_equals_literal(name, "func_num_args")) {
67
+ return ZEND_FUNC_VARARG;
68
+ } else if (zend_string_equals_literal(name, "func_get_arg")) {
69
+ return ZEND_FUNC_VARARG;
70
+ } else if (zend_string_equals_literal(name, "func_get_args")) {
71
+ return ZEND_FUNC_VARARG;
72
+ } else {
73
+ return 0;
74
+ }
75
+ }
76
+
77
+ static void zend_mark_reachable(zend_op *opcodes, zend_cfg *cfg, zend_basic_block *b) /* {{{ */
78
+ {
79
+ zend_basic_block *blocks = cfg->blocks;
80
+
81
+ while (1) {
82
+ int i;
83
+
84
+ b->flags |= ZEND_BB_REACHABLE;
85
+ if (b->successors_count == 0) {
86
+ b->flags |= ZEND_BB_EXIT;
87
+ return;
88
+ }
89
+
90
+ for (i = 0; i < b->successors_count; i++) {
91
+ zend_basic_block *succ = blocks + b->successors[i];
92
+
93
+ if (b->len != 0) {
94
+ zend_uchar opcode = opcodes[b->start + b->len - 1].opcode;
95
+ if (b->successors_count == 1) {
96
+ if (opcode == ZEND_JMP) {
97
+ succ->flags |= ZEND_BB_TARGET;
98
+ } else {
99
+ succ->flags |= ZEND_BB_FOLLOW;
100
+
101
+ if ((cfg->flags & ZEND_CFG_STACKLESS)) {
102
+ if (opcode == ZEND_INCLUDE_OR_EVAL ||
103
+ opcode == ZEND_GENERATOR_CREATE ||
104
+ opcode == ZEND_YIELD ||
105
+ opcode == ZEND_YIELD_FROM ||
106
+ opcode == ZEND_DO_FCALL ||
107
+ opcode == ZEND_DO_UCALL ||
108
+ opcode == ZEND_DO_FCALL_BY_NAME) {
109
+ succ->flags |= ZEND_BB_ENTRY;
110
+ }
111
+ }
112
+ if ((cfg->flags & ZEND_CFG_RECV_ENTRY)) {
113
+ if (opcode == ZEND_RECV ||
114
+ opcode == ZEND_RECV_INIT) {
115
+ succ->flags |= ZEND_BB_RECV_ENTRY;
116
+ }
117
+ }
118
+ }
119
+ } else if (b->successors_count == 2) {
120
+ if (i == 0 || opcode == ZEND_JMPZNZ) {
121
+ succ->flags |= ZEND_BB_TARGET;
122
+ } else {
123
+ succ->flags |= ZEND_BB_FOLLOW;
124
+ }
125
+ } else {
126
+ ZEND_ASSERT(opcode == ZEND_SWITCH_LONG || opcode == ZEND_SWITCH_STRING);
127
+ if (i == b->successors_count - 1) {
128
+ succ->flags |= ZEND_BB_FOLLOW | ZEND_BB_TARGET;
129
+ } else {
130
+ succ->flags |= ZEND_BB_TARGET;
131
+ }
132
+ }
133
+ } else {
134
+ succ->flags |= ZEND_BB_FOLLOW;
135
+ }
136
+
137
+ if (i == b->successors_count - 1) {
138
+ /* Tail call optimization */
139
+ if (succ->flags & ZEND_BB_REACHABLE) {
140
+ return;
141
+ }
142
+
143
+ b = succ;
144
+ break;
145
+ } else {
146
+ /* Recusively check reachability */
147
+ if (!(succ->flags & ZEND_BB_REACHABLE)) {
148
+ zend_mark_reachable(opcodes, cfg, succ);
149
+ }
150
+ }
151
+ }
152
+ }
153
+ }
154
+ /* }}} */
155
+
156
+ static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg, int start) /* {{{ */
157
+ {
158
+ zend_basic_block *blocks = cfg->blocks;
159
+
160
+ blocks[start].flags = ZEND_BB_START;
161
+ zend_mark_reachable(op_array->opcodes, cfg, blocks + start);
162
+
163
+ if (op_array->last_live_range || op_array->last_try_catch) {
164
+ zend_basic_block *b;
165
+ int j, changed;
166
+ uint32_t *block_map = cfg->map;
167
+
168
+ do {
169
+ changed = 0;
170
+
171
+ /* Add live range paths */
172
+ for (j = 0; j < op_array->last_live_range; j++) {
173
+ zend_live_range *live_range = &op_array->live_range[j];
174
+ if (live_range->var == (uint32_t)-1) {
175
+ /* this live range already removed */
176
+ continue;
177
+ }
178
+ b = blocks + block_map[live_range->start];
179
+ if (b->flags & ZEND_BB_REACHABLE) {
180
+ while (b->len > 0 && op_array->opcodes[b->start].opcode == ZEND_NOP) {
181
+ /* check if NOP breaks incorrect smart branch */
182
+ if (b->len == 2
183
+ && (op_array->opcodes[b->start + 1].opcode == ZEND_JMPZ
184
+ || op_array->opcodes[b->start + 1].opcode == ZEND_JMPNZ)
185
+ && (op_array->opcodes[b->start + 1].op1_type & (IS_CV|IS_CONST))
186
+ && b->start > 0
187
+ && zend_is_smart_branch(op_array->opcodes + b->start - 1)) {
188
+ break;
189
+ }
190
+ b->start++;
191
+ b->len--;
192
+ }
193
+ if (b->len == 0 && (uint32_t)b->successors[0] == block_map[live_range->end]) {
194
+ /* mark as removed (empty live range) */
195
+ live_range->var = (uint32_t)-1;
196
+ continue;
197
+ }
198
+ b->flags |= ZEND_BB_GEN_VAR;
199
+ b = blocks + block_map[live_range->end];
200
+ b->flags |= ZEND_BB_KILL_VAR;
201
+ if (!(b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE))) {
202
+ if ((cfg->flags & ZEND_CFG_SPLIT_AT_LIVE_RANGES)) {
203
+ changed = 1;
204
+ zend_mark_reachable(op_array->opcodes, cfg, b);
205
+ } else {
206
+ b->flags |= ZEND_BB_UNREACHABLE_FREE;
207
+ }
208
+ }
209
+ } else {
210
+ ZEND_ASSERT(!(blocks[block_map[live_range->end]].flags & ZEND_BB_REACHABLE));
211
+ }
212
+ }
213
+
214
+ /* Add exception paths */
215
+ for (j = 0; j < op_array->last_try_catch; j++) {
216
+
217
+ /* check for jumps into the middle of try block */
218
+ b = blocks + block_map[op_array->try_catch_array[j].try_op];
219
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
220
+ zend_basic_block *end;
221
+
222
+ if (op_array->try_catch_array[j].catch_op) {
223
+ end = blocks + block_map[op_array->try_catch_array[j].catch_op];
224
+ while (b != end) {
225
+ if (b->flags & ZEND_BB_REACHABLE) {
226
+ op_array->try_catch_array[j].try_op = b->start;
227
+ break;
228
+ }
229
+ b++;
230
+ }
231
+ }
232
+ b = blocks + block_map[op_array->try_catch_array[j].try_op];
233
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
234
+ if (op_array->try_catch_array[j].finally_op) {
235
+ end = blocks + block_map[op_array->try_catch_array[j].finally_op];
236
+ while (b != end) {
237
+ if (b->flags & ZEND_BB_REACHABLE) {
238
+ op_array->try_catch_array[j].try_op = op_array->try_catch_array[j].catch_op;
239
+ changed = 1;
240
+ zend_mark_reachable(op_array->opcodes, cfg, blocks + block_map[op_array->try_catch_array[j].try_op]);
241
+ break;
242
+ }
243
+ b++;
244
+ }
245
+ }
246
+ }
247
+ }
248
+
249
+ b = blocks + block_map[op_array->try_catch_array[j].try_op];
250
+ if (b->flags & ZEND_BB_REACHABLE) {
251
+ b->flags |= ZEND_BB_TRY;
252
+ if (op_array->try_catch_array[j].catch_op) {
253
+ b = blocks + block_map[op_array->try_catch_array[j].catch_op];
254
+ b->flags |= ZEND_BB_CATCH;
255
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
256
+ changed = 1;
257
+ zend_mark_reachable(op_array->opcodes, cfg, b);
258
+ }
259
+ }
260
+ if (op_array->try_catch_array[j].finally_op) {
261
+ b = blocks + block_map[op_array->try_catch_array[j].finally_op];
262
+ b->flags |= ZEND_BB_FINALLY;
263
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
264
+ changed = 1;
265
+ zend_mark_reachable(op_array->opcodes, cfg, b);
266
+ }
267
+ }
268
+ if (op_array->try_catch_array[j].finally_end) {
269
+ b = blocks + block_map[op_array->try_catch_array[j].finally_end];
270
+ b->flags |= ZEND_BB_FINALLY_END;
271
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
272
+ changed = 1;
273
+ zend_mark_reachable(op_array->opcodes, cfg, b);
274
+ }
275
+ }
276
+ } else {
277
+ if (op_array->try_catch_array[j].catch_op) {
278
+ ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].catch_op]].flags & ZEND_BB_REACHABLE));
279
+ }
280
+ if (op_array->try_catch_array[j].finally_op) {
281
+ ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_op]].flags & ZEND_BB_REACHABLE));
282
+ }
283
+ if (op_array->try_catch_array[j].finally_end) {
284
+ ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_end]].flags & ZEND_BB_REACHABLE));
285
+ }
286
+ }
287
+ }
288
+ } while (changed);
289
+ }
290
+ }
291
+ /* }}} */
292
+
293
+ static void initialize_block(zend_basic_block *block) {
294
+ block->flags = 0;
295
+ block->successors = block->successors_storage;
296
+ block->successors_count = 0;
297
+ block->predecessors_count = 0;
298
+ block->predecessor_offset = -1;
299
+ block->idom = -1;
300
+ block->loop_header = -1;
301
+ block->level = -1;
302
+ block->children = -1;
303
+ block->next_child = -1;
304
+ }
305
+
306
+ #define BB_START(i) do { \
307
+ if (!block_map[i]) { blocks_count++;} \
308
+ block_map[i]++; \
309
+ } while (0)
310
+
311
+ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg) /* {{{ */
312
+ {
313
+ uint32_t flags = 0;
314
+ uint32_t i;
315
+ int j;
316
+ uint32_t *block_map;
317
+ zend_function *fn;
318
+ int blocks_count = 0;
319
+ zend_basic_block *blocks;
320
+ zval *zv;
321
+ zend_bool extra_entry_block = 0;
322
+
323
+ cfg->flags = build_flags & (ZEND_CFG_SPLIT_AT_LIVE_RANGES|ZEND_CFG_STACKLESS|ZEND_CFG_RECV_ENTRY);
324
+
325
+ cfg->map = block_map = zend_arena_calloc(arena, op_array->last, sizeof(uint32_t));
326
+
327
+ /* Build CFG, Step 1: Find basic blocks starts, calculate number of blocks */
328
+ BB_START(0);
329
+ for (i = 0; i < op_array->last; i++) {
330
+ zend_op *opline = op_array->opcodes + i;
331
+ switch (opline->opcode) {
332
+ case ZEND_RECV:
333
+ case ZEND_RECV_INIT:
334
+ if (build_flags & ZEND_CFG_RECV_ENTRY) {
335
+ BB_START(i + 1);
336
+ }
337
+ break;
338
+ case ZEND_RETURN:
339
+ case ZEND_RETURN_BY_REF:
340
+ case ZEND_GENERATOR_RETURN:
341
+ case ZEND_EXIT:
342
+ case ZEND_THROW:
343
+ if (i + 1 < op_array->last) {
344
+ BB_START(i + 1);
345
+ }
346
+ break;
347
+ case ZEND_INCLUDE_OR_EVAL:
348
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
349
+ case ZEND_GENERATOR_CREATE:
350
+ case ZEND_YIELD:
351
+ case ZEND_YIELD_FROM:
352
+ if (build_flags & ZEND_CFG_STACKLESS) {
353
+ BB_START(i + 1);
354
+ }
355
+ break;
356
+ case ZEND_DO_FCALL:
357
+ case ZEND_DO_UCALL:
358
+ case ZEND_DO_FCALL_BY_NAME:
359
+ flags |= ZEND_FUNC_HAS_CALLS;
360
+ if (build_flags & ZEND_CFG_STACKLESS) {
361
+ BB_START(i + 1);
362
+ }
363
+ break;
364
+ case ZEND_DO_ICALL:
365
+ flags |= ZEND_FUNC_HAS_CALLS;
366
+ break;
367
+ case ZEND_INIT_FCALL:
368
+ case ZEND_INIT_NS_FCALL_BY_NAME:
369
+ zv = CRT_CONSTANT(opline->op2);
370
+ if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
371
+ /* The third literal is the lowercased unqualified name */
372
+ zv += 2;
373
+ }
374
+ if ((fn = zend_hash_find_ptr(EG(function_table), Z_STR_P(zv))) != NULL) {
375
+ if (fn->type == ZEND_INTERNAL_FUNCTION) {
376
+ flags |= zend_cfg_classify_function(
377
+ Z_STR_P(zv), opline->extended_value);
378
+ }
379
+ }
380
+ break;
381
+ case ZEND_FAST_CALL:
382
+ BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
383
+ BB_START(i + 1);
384
+ break;
385
+ case ZEND_FAST_RET:
386
+ if (i + 1 < op_array->last) {
387
+ BB_START(i + 1);
388
+ }
389
+ break;
390
+ case ZEND_JMP:
391
+ BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
392
+ if (i + 1 < op_array->last) {
393
+ BB_START(i + 1);
394
+ }
395
+ break;
396
+ case ZEND_JMPZNZ:
397
+ BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
398
+ BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
399
+ if (i + 1 < op_array->last) {
400
+ BB_START(i + 1);
401
+ }
402
+ break;
403
+ case ZEND_JMPZ:
404
+ case ZEND_JMPNZ:
405
+ case ZEND_JMPZ_EX:
406
+ case ZEND_JMPNZ_EX:
407
+ case ZEND_JMP_SET:
408
+ case ZEND_COALESCE:
409
+ case ZEND_ASSERT_CHECK:
410
+ BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
411
+ BB_START(i + 1);
412
+ break;
413
+ case ZEND_CATCH:
414
+ if (!(opline->extended_value & ZEND_LAST_CATCH)) {
415
+ BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
416
+ }
417
+ BB_START(i + 1);
418
+ break;
419
+ case ZEND_DECLARE_ANON_CLASS:
420
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
421
+ case ZEND_FE_FETCH_R:
422
+ case ZEND_FE_FETCH_RW:
423
+ BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
424
+ BB_START(i + 1);
425
+ break;
426
+ case ZEND_FE_RESET_R:
427
+ case ZEND_FE_RESET_RW:
428
+ BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
429
+ BB_START(i + 1);
430
+ break;
431
+ case ZEND_SWITCH_LONG:
432
+ case ZEND_SWITCH_STRING:
433
+ {
434
+ HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2));
435
+ zval *zv;
436
+ ZEND_HASH_FOREACH_VAL(jumptable, zv) {
437
+ BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)));
438
+ } ZEND_HASH_FOREACH_END();
439
+ BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
440
+ BB_START(i + 1);
441
+ break;
442
+ }
443
+ case ZEND_UNSET_VAR:
444
+ case ZEND_ISSET_ISEMPTY_VAR:
445
+ if (opline->extended_value & ZEND_FETCH_LOCAL) {
446
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
447
+ } else if ((opline->extended_value & (ZEND_FETCH_GLOBAL | ZEND_FETCH_GLOBAL_LOCK)) &&
448
+ !op_array->function_name) {
449
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
450
+ }
451
+ break;
452
+ case ZEND_FETCH_R:
453
+ case ZEND_FETCH_W:
454
+ case ZEND_FETCH_RW:
455
+ case ZEND_FETCH_FUNC_ARG:
456
+ case ZEND_FETCH_IS:
457
+ case ZEND_FETCH_UNSET:
458
+ if (opline->extended_value & ZEND_FETCH_LOCAL) {
459
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
460
+ } else if ((opline->extended_value & (ZEND_FETCH_GLOBAL | ZEND_FETCH_GLOBAL_LOCK)) &&
461
+ !op_array->function_name) {
462
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
463
+ }
464
+ break;
465
+ case ZEND_FUNC_GET_ARGS:
466
+ flags |= ZEND_FUNC_VARARG;
467
+ break;
468
+ case ZEND_EXT_NOP:
469
+ case ZEND_EXT_STMT:
470
+ case ZEND_EXT_FCALL_BEGIN:
471
+ case ZEND_EXT_FCALL_END:
472
+ flags |= ZEND_FUNC_HAS_EXTENDED_INFO;
473
+ break;
474
+ }
475
+ }
476
+
477
+ /* If the entry block has predecessors, we may need to split it */
478
+ if ((build_flags & ZEND_CFG_NO_ENTRY_PREDECESSORS)
479
+ && op_array->last > 0 && block_map[0] > 1) {
480
+ extra_entry_block = 1;
481
+ }
482
+
483
+ if ((cfg->flags & ZEND_CFG_SPLIT_AT_LIVE_RANGES)) {
484
+ for (j = 0; j < op_array->last_live_range; j++) {
485
+ BB_START(op_array->live_range[j].start);
486
+ BB_START(op_array->live_range[j].end);
487
+ }
488
+ }
489
+
490
+ if (op_array->last_try_catch) {
491
+ for (j = 0; j < op_array->last_try_catch; j++) {
492
+ BB_START(op_array->try_catch_array[j].try_op);
493
+ if (op_array->try_catch_array[j].catch_op) {
494
+ BB_START(op_array->try_catch_array[j].catch_op);
495
+ }
496
+ if (op_array->try_catch_array[j].finally_op) {
497
+ BB_START(op_array->try_catch_array[j].finally_op);
498
+ }
499
+ if (op_array->try_catch_array[j].finally_end) {
500
+ BB_START(op_array->try_catch_array[j].finally_end);
501
+ }
502
+ }
503
+ }
504
+
505
+ blocks_count += extra_entry_block;
506
+ cfg->blocks_count = blocks_count;
507
+
508
+ /* Build CFG, Step 2: Build Array of Basic Blocks */
509
+ cfg->blocks = blocks = zend_arena_calloc(arena, sizeof(zend_basic_block), blocks_count);
510
+
511
+ blocks_count = -1;
512
+
513
+ if (extra_entry_block) {
514
+ initialize_block(&blocks[0]);
515
+ blocks[0].start = 0;
516
+ blocks[0].len = 0;
517
+ blocks_count++;
518
+ }
519
+
520
+ for (i = 0; i < op_array->last; i++) {
521
+ if (block_map[i]) {
522
+ if (blocks_count >= 0) {
523
+ blocks[blocks_count].len = i - blocks[blocks_count].start;
524
+ }
525
+ blocks_count++;
526
+ initialize_block(&blocks[blocks_count]);
527
+ blocks[blocks_count].start = i;
528
+ }
529
+ block_map[i] = blocks_count;
530
+ }
531
+
532
+ blocks[blocks_count].len = i - blocks[blocks_count].start;
533
+ blocks_count++;
534
+
535
+ /* Build CFG, Step 3: Calculate successors */
536
+ for (j = 0; j < blocks_count; j++) {
537
+ zend_basic_block *block = &blocks[j];
538
+ zend_op *opline;
539
+ if (block->len == 0) {
540
+ block->successors_count = 1;
541
+ block->successors[0] = j + 1;
542
+ continue;
543
+ }
544
+
545
+ opline = op_array->opcodes + block->start + block->len - 1;
546
+ switch (opline->opcode) {
547
+ case ZEND_FAST_RET:
548
+ case ZEND_RETURN:
549
+ case ZEND_RETURN_BY_REF:
550
+ case ZEND_GENERATOR_RETURN:
551
+ case ZEND_EXIT:
552
+ case ZEND_THROW:
553
+ break;
554
+ case ZEND_JMP:
555
+ block->successors_count = 1;
556
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes];
557
+ break;
558
+ case ZEND_JMPZNZ:
559
+ block->successors_count = 2;
560
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
561
+ block->successors[1] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
562
+ break;
563
+ case ZEND_JMPZ:
564
+ case ZEND_JMPNZ:
565
+ case ZEND_JMPZ_EX:
566
+ case ZEND_JMPNZ_EX:
567
+ case ZEND_JMP_SET:
568
+ case ZEND_COALESCE:
569
+ case ZEND_ASSERT_CHECK:
570
+ block->successors_count = 2;
571
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
572
+ block->successors[1] = j + 1;
573
+ break;
574
+ case ZEND_CATCH:
575
+ if (!(opline->extended_value & ZEND_LAST_CATCH)) {
576
+ block->successors_count = 2;
577
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
578
+ block->successors[1] = j + 1;
579
+ } else {
580
+ block->successors_count = 1;
581
+ block->successors[0] = j + 1;
582
+ }
583
+ break;
584
+ case ZEND_DECLARE_ANON_CLASS:
585
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
586
+ case ZEND_FE_FETCH_R:
587
+ case ZEND_FE_FETCH_RW:
588
+ block->successors_count = 2;
589
+ block->successors[0] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
590
+ block->successors[1] = j + 1;
591
+ break;
592
+ case ZEND_FE_RESET_R:
593
+ case ZEND_FE_RESET_RW:
594
+ block->successors_count = 2;
595
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
596
+ block->successors[1] = j + 1;
597
+ break;
598
+ case ZEND_FAST_CALL:
599
+ block->successors_count = 2;
600
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes];
601
+ block->successors[1] = j + 1;
602
+ break;
603
+ case ZEND_SWITCH_LONG:
604
+ case ZEND_SWITCH_STRING:
605
+ {
606
+ HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2));
607
+ zval *zv;
608
+ uint32_t s = 0;
609
+
610
+ block->successors_count = 2 + zend_hash_num_elements(jumptable);
611
+ block->successors = zend_arena_calloc(arena, block->successors_count, sizeof(int));
612
+
613
+ ZEND_HASH_FOREACH_VAL(jumptable, zv) {
614
+ block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))];
615
+ } ZEND_HASH_FOREACH_END();
616
+
617
+ block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
618
+ block->successors[s++] = j + 1;
619
+ break;
620
+ }
621
+ default:
622
+ block->successors_count = 1;
623
+ block->successors[0] = j + 1;
624
+ break;
625
+ }
626
+ }
627
+
628
+ /* Build CFG, Step 4, Mark Reachable Basic Blocks */
629
+ zend_mark_reachable_blocks(op_array, cfg, 0);
630
+
631
+ cfg->flags |= flags;
632
+
633
+ return SUCCESS;
634
+ }
635
+ /* }}} */
636
+
637
+ /*
638
+ * Local variables:
639
+ * tab-width: 4
640
+ * c-basic-offset: 4
641
+ * indent-tabs-mode: t
642
+ * End:
643
+ */
pcov-1.0.11/pcov-1.0.11/cfg/703/zend_cfg.h ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ +----------------------------------------------------------------------+
3
+ | Zend Engine, CFG - Control Flow Graph |
4
+ +----------------------------------------------------------------------+
5
+ | Copyright (c) 1998-2018 The PHP Group |
6
+ +----------------------------------------------------------------------+
7
+ | This source file is subject to version 3.01 of the PHP license, |
8
+ | that is bundled with this package in the file LICENSE, and is |
9
+ | available through the world-wide-web at the following url: |
10
+ | http://www.php.net/license/3_01.txt |
11
+ | If you did not receive a copy of the PHP license and are unable to |
12
+ | obtain it through the world-wide-web, please send a note to |
13
+ | [email protected] so we can mail you a copy immediately. |
14
+ +----------------------------------------------------------------------+
15
+ | Authors: Dmitry Stogov <[email protected]> |
16
+ +----------------------------------------------------------------------+
17
+ */
18
+
19
+ #ifndef ZEND_CFG_H
20
+ #define ZEND_CFG_H
21
+
22
+ /* zend_basic_bloc.flags */
23
+ #define ZEND_BB_START (1<<0) /* fist block */
24
+ #define ZEND_BB_FOLLOW (1<<1) /* follows the next block */
25
+ #define ZEND_BB_TARGET (1<<2) /* jump taget */
26
+ #define ZEND_BB_EXIT (1<<3) /* without successors */
27
+ #define ZEND_BB_ENTRY (1<<4) /* stackless entry */
28
+ #define ZEND_BB_TRY (1<<5) /* start of try block */
29
+ #define ZEND_BB_CATCH (1<<6) /* start of catch block */
30
+ #define ZEND_BB_FINALLY (1<<7) /* start of finally block */
31
+ #define ZEND_BB_FINALLY_END (1<<8) /* end of finally block */
32
+ #define ZEND_BB_GEN_VAR (1<<9) /* start of live range */
33
+ #define ZEND_BB_KILL_VAR (1<<10) /* end of live range */
34
+ #define ZEND_BB_UNREACHABLE_FREE (1<<11) /* unreachable loop free */
35
+ #define ZEND_BB_RECV_ENTRY (1<<12) /* RECV entry */
36
+
37
+ #define ZEND_BB_LOOP_HEADER (1<<16)
38
+ #define ZEND_BB_IRREDUCIBLE_LOOP (1<<17)
39
+
40
+ #define ZEND_BB_REACHABLE (1<<31)
41
+
42
+ #define ZEND_BB_PROTECTED (ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY|ZEND_BB_TRY|ZEND_BB_CATCH|ZEND_BB_FINALLY|ZEND_BB_FINALLY_END|ZEND_BB_GEN_VAR|ZEND_BB_KILL_VAR)
43
+
44
+ typedef struct _zend_basic_block {
45
+ int *successors; /* successor block indices */
46
+ uint32_t flags;
47
+ uint32_t start; /* first opcode number */
48
+ uint32_t len; /* number of opcodes */
49
+ int successors_count; /* number of successors */
50
+ int predecessors_count; /* number of predecessors */
51
+ int predecessor_offset; /* offset of 1-st predecessor */
52
+ int idom; /* immediate dominator block */
53
+ int loop_header; /* closest loop header, or -1 */
54
+ int level; /* steps away from the entry in the dom. tree */
55
+ int children; /* list of dominated blocks */
56
+ int next_child; /* next dominated block */
57
+ int successors_storage[2]; /* up to 2 successor blocks */
58
+ } zend_basic_block;
59
+
60
+ /*
61
+ +------------+---+---+---+---+---+
62
+ | |OP1|OP2|EXT| 0 | 1 |
63
+ +------------+---+---+---+---+---+
64
+ |JMP |ADR| | |OP1| - |
65
+ |JMPZ | |ADR| |OP2|FOL|
66
+ |JMPNZ | |ADR| |OP2|FOL|
67
+ |JMPZNZ | |ADR|ADR|OP2|EXT|
68
+ |JMPZ_EX | |ADR| |OP2|FOL|
69
+ |JMPNZ_EX | |ADR| |OP2|FOL|
70
+ |JMP_SET | |ADR| |OP2|FOL|
71
+ |COALESCE | |ADR| |OP2|FOL|
72
+ |ASSERT_CHK | |ADR| |OP2|FOL|
73
+ |NEW | |ADR| |OP2|FOL|
74
+ |DCL_ANON* |ADR| | |OP1|FOL|
75
+ |FE_RESET_* | |ADR| |OP2|FOL|
76
+ |FE_FETCH_* | | |ADR|EXT|FOL|
77
+ |CATCH | | |ADR|EXT|FOL|
78
+ |FAST_CALL |ADR| | |OP1|FOL|
79
+ |FAST_RET | | | | - | - |
80
+ |RETURN* | | | | - | - |
81
+ |EXIT | | | | - | - |
82
+ |THROW | | | | - | - |
83
+ |* | | | |FOL| - |
84
+ +------------+---+---+---+---+---+
85
+ */
86
+
87
+ typedef struct _zend_cfg {
88
+ int blocks_count; /* number of basic blocks */
89
+ int edges_count; /* number of edges */
90
+ zend_basic_block *blocks; /* array of basic blocks */
91
+ int *predecessors;
92
+ uint32_t *map;
93
+ uint32_t flags;
94
+ } zend_cfg;
95
+
96
+ /* Build Flags */
97
+ #define ZEND_RT_CONSTANTS (1<<31)
98
+ #define ZEND_CFG_STACKLESS (1<<30)
99
+ #define ZEND_SSA_DEBUG_LIVENESS (1<<29)
100
+ #define ZEND_SSA_DEBUG_PHI_PLACEMENT (1<<28)
101
+ #define ZEND_SSA_RC_INFERENCE (1<<27)
102
+ #define ZEND_CFG_SPLIT_AT_LIVE_RANGES (1<<26)
103
+ #define ZEND_CFG_NO_ENTRY_PREDECESSORS (1<<25)
104
+ #define ZEND_CFG_RECV_ENTRY (1<<24)
105
+ #define ZEND_CALL_TREE (1<<23)
106
+ #define ZEND_SSA_USE_CV_RESULTS (1<<22)
107
+
108
+ #define CRT_CONSTANT_EX(op_array, opline, node, rt_constants) \
109
+ ((rt_constants) ? \
110
+ RT_CONSTANT(opline, (node)) \
111
+ : \
112
+ CT_CONSTANT_EX(op_array, (node).constant) \
113
+ )
114
+
115
+ #define CRT_CONSTANT(node) \
116
+ CRT_CONSTANT_EX(op_array, opline, node, (build_flags & ZEND_RT_CONSTANTS))
117
+
118
+ #define RETURN_VALUE_USED(opline) \
119
+ ((opline)->result_type != IS_UNUSED)
120
+
121
+ BEGIN_EXTERN_C()
122
+
123
+ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg);
124
+
125
+ END_EXTERN_C()
126
+
127
+ #endif /* ZEND_CFG_H */
128
+
129
+ /*
130
+ * Local variables:
131
+ * tab-width: 4
132
+ * c-basic-offset: 4
133
+ * indent-tabs-mode: t
134
+ * End:
135
+ */
pcov-1.0.11/pcov-1.0.11/cfg/703/zend_worklist.h ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ +----------------------------------------------------------------------+
3
+ | Zend Engine |
4
+ +----------------------------------------------------------------------+
5
+ | Copyright (c) 1998-2018 The PHP Group |
6
+ +----------------------------------------------------------------------+
7
+ | This source file is subject to version 3.01 of the PHP license, |
8
+ | that is bundled with this package in the file LICENSE, and is |
9
+ | available through the world-wide-web at the following url: |
10
+ | http://www.php.net/license/3_01.txt |
11
+ | If you did not receive a copy of the PHP license and are unable to |
12
+ | obtain it through the world-wide-web, please send a note to |
13
+ | [email protected] so we can mail you a copy immediately. |
14
+ +----------------------------------------------------------------------+
15
+ | Authors: Andy Wingo <[email protected]> |
16
+ +----------------------------------------------------------------------+
17
+ */
18
+
19
+ #ifndef _ZEND_WORKLIST_H_
20
+ #define _ZEND_WORKLIST_H_
21
+
22
+ #include "zend_arena.h"
23
+ #include "zend_bitset.h"
24
+
25
+ typedef struct _zend_worklist_stack {
26
+ int *buf;
27
+ int len;
28
+ int capacity;
29
+ } zend_worklist_stack;
30
+
31
+ #define ZEND_WORKLIST_STACK_ALLOCA(s, _len, use_heap) do { \
32
+ (s)->buf = (int*)do_alloca(sizeof(int) * _len, use_heap); \
33
+ (s)->len = 0; \
34
+ (s)->capacity = _len; \
35
+ } while (0)
36
+
37
+ #define ZEND_WORKLIST_STACK_FREE_ALLOCA(s, use_heap) \
38
+ free_alloca((s)->buf, use_heap)
39
+
40
+ static inline int zend_worklist_stack_prepare(zend_arena **arena, zend_worklist_stack *stack, int len)
41
+ {
42
+ ZEND_ASSERT(len >= 0);
43
+
44
+ stack->buf = (int*)zend_arena_calloc(arena, sizeof(*stack->buf), len);
45
+ stack->len = 0;
46
+ stack->capacity = len;
47
+
48
+ return SUCCESS;
49
+ }
50
+
51
+ static inline void zend_worklist_stack_push(zend_worklist_stack *stack, int i)
52
+ {
53
+ ZEND_ASSERT(stack->len < stack->capacity);
54
+ stack->buf[stack->len++] = i;
55
+ }
56
+
57
+ static inline int zend_worklist_stack_peek(zend_worklist_stack *stack)
58
+ {
59
+ ZEND_ASSERT(stack->len);
60
+ return stack->buf[stack->len - 1];
61
+ }
62
+
63
+ static inline int zend_worklist_stack_pop(zend_worklist_stack *stack)
64
+ {
65
+ ZEND_ASSERT(stack->len);
66
+ return stack->buf[--stack->len];
67
+ }
68
+
69
+ typedef struct _zend_worklist {
70
+ zend_bitset visited;
71
+ zend_worklist_stack stack;
72
+ } zend_worklist;
73
+
74
+ #define ZEND_WORKLIST_ALLOCA(w, _len, use_heap) do { \
75
+ (w)->stack.buf = (int*)do_alloca(ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len) + sizeof(zend_ulong) * zend_bitset_len(_len), use_heap); \
76
+ (w)->stack.len = 0; \
77
+ (w)->stack.capacity = _len; \
78
+ (w)->visited = (zend_bitset)((char*)(w)->stack.buf + ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len)); \
79
+ memset((w)->visited, 0, sizeof(zend_ulong) * zend_bitset_len(_len)); \
80
+ } while (0)
81
+
82
+ #define ZEND_WORKLIST_FREE_ALLOCA(w, use_heap) \
83
+ free_alloca((w)->stack.buf, use_heap)
84
+
85
+ static inline int zend_worklist_prepare(zend_arena **arena, zend_worklist *worklist, int len)
86
+ {
87
+ ZEND_ASSERT(len >= 0);
88
+ worklist->visited = (zend_bitset)zend_arena_calloc(arena, sizeof(zend_ulong), zend_bitset_len(len));
89
+ return zend_worklist_stack_prepare(arena, &worklist->stack, len);
90
+ }
91
+
92
+ static inline int zend_worklist_len(zend_worklist *worklist)
93
+ {
94
+ return worklist->stack.len;
95
+ }
96
+
97
+ static inline int zend_worklist_push(zend_worklist *worklist, int i)
98
+ {
99
+ ZEND_ASSERT(i >= 0 && i < worklist->stack.capacity);
100
+
101
+ if (zend_bitset_in(worklist->visited, i)) {
102
+ return 0;
103
+ }
104
+
105
+ zend_bitset_incl(worklist->visited, i);
106
+ zend_worklist_stack_push(&worklist->stack, i);
107
+ return 1;
108
+ }
109
+
110
+ static inline int zend_worklist_peek(zend_worklist *worklist)
111
+ {
112
+ return zend_worklist_stack_peek(&worklist->stack);
113
+ }
114
+
115
+ static inline int zend_worklist_pop(zend_worklist *worklist)
116
+ {
117
+ /* Does not clear visited flag */
118
+ return zend_worklist_stack_pop(&worklist->stack);
119
+ }
120
+
121
+ #endif /* _ZEND_WORKLIST_H_ */
122
+
123
+ /*
124
+ * Local variables:
125
+ * tab-width: 4
126
+ * c-basic-offset: 4
127
+ * indent-tabs-mode: t
128
+ * End:
129
+ */
pcov-1.0.11/pcov-1.0.11/cfg/704/zend_cfg.c ADDED
@@ -0,0 +1,612 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ +----------------------------------------------------------------------+
3
+ | Zend Engine, CFG - Control Flow Graph |
4
+ +----------------------------------------------------------------------+
5
+ | Copyright (c) The PHP Group |
6
+ +----------------------------------------------------------------------+
7
+ | This source file is subject to version 3.01 of the PHP license, |
8
+ | that is bundled with this package in the file LICENSE, and is |
9
+ | available through the world-wide-web at the following url: |
10
+ | http://www.php.net/license/3_01.txt |
11
+ | If you did not receive a copy of the PHP license and are unable to |
12
+ | obtain it through the world-wide-web, please send a note to |
13
+ | [email protected] so we can mail you a copy immediately. |
14
+ +----------------------------------------------------------------------+
15
+ | Authors: Dmitry Stogov <[email protected]> |
16
+ +----------------------------------------------------------------------+
17
+
18
+ This source file has been adapted for pcov so that the CFG from O+ is standalone
19
+ */
20
+
21
+ #include "php.h"
22
+ #include "zend_compile.h"
23
+ #include "zend_cfg.h"
24
+ #include "zend_worklist.h"
25
+
26
+ /* func flags */
27
+ #define ZEND_FUNC_INDIRECT_VAR_ACCESS (1<<0) /* accesses variables by name */
28
+ #define ZEND_FUNC_HAS_CALLS (1<<1)
29
+ #define ZEND_FUNC_VARARG (1<<2) /* uses func_get_args() */
30
+ #define ZEND_FUNC_NO_LOOPS (1<<3)
31
+ #define ZEND_FUNC_IRREDUCIBLE (1<<4)
32
+ #define ZEND_FUNC_FREE_LOOP_VAR (1<<5)
33
+ #define ZEND_FUNC_RECURSIVE (1<<7)
34
+ #define ZEND_FUNC_RECURSIVE_DIRECTLY (1<<8)
35
+ #define ZEND_FUNC_RECURSIVE_INDIRECTLY (1<<9)
36
+ #define ZEND_FUNC_HAS_EXTENDED_FCALL (1<<10)
37
+ #define ZEND_FUNC_HAS_EXTENDED_STMT (1<<11)
38
+
39
+ /* The following flags are valid only for return values of internal functions
40
+ * returned by zend_get_func_info()
41
+ */
42
+ #define FUNC_MAY_WARN (1<<30)
43
+
44
+ typedef struct _zend_func_info zend_func_info;
45
+ typedef struct _zend_call_info zend_call_info;
46
+
47
+ #define ZEND_FUNC_INFO(op_array) \
48
+ ((zend_func_info*)((op_array)->reserved[zend_func_info_rid]))
49
+
50
+ #define ZEND_SET_FUNC_INFO(op_array, info) do { \
51
+ zend_func_info** pinfo = (zend_func_info**)&(op_array)->reserved[zend_func_info_rid]; \
52
+ *pinfo = info; \
53
+ } while (0)
54
+
55
+ static uint32_t zend_cfg_classify_function(zend_string *name, uint32_t num_args) {
56
+ if (zend_string_equals_literal(name, "extract")) {
57
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
58
+ } else if (zend_string_equals_literal(name, "compact")) {
59
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
60
+ } else if (zend_string_equals_literal(name, "parse_str") && num_args <= 1) {
61
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
62
+ } else if (zend_string_equals_literal(name, "mb_parse_str") && num_args <= 1) {
63
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
64
+ } else if (zend_string_equals_literal(name, "get_defined_vars")) {
65
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
66
+ } else if (zend_string_equals_literal(name, "assert")) {
67
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
68
+ } else if (zend_string_equals_literal(name, "db2_execute")) {
69
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
70
+ } else if (zend_string_equals_literal(name, "func_num_args")) {
71
+ return ZEND_FUNC_VARARG;
72
+ } else if (zend_string_equals_literal(name, "func_get_arg")) {
73
+ return ZEND_FUNC_VARARG;
74
+ } else if (zend_string_equals_literal(name, "func_get_args")) {
75
+ return ZEND_FUNC_VARARG;
76
+ } else {
77
+ return 0;
78
+ }
79
+ }
80
+
81
+ static void zend_mark_reachable(zend_op *opcodes, zend_cfg *cfg, zend_basic_block *b) /* {{{ */
82
+ {
83
+ zend_basic_block *blocks = cfg->blocks;
84
+
85
+ while (1) {
86
+ int i;
87
+
88
+ b->flags |= ZEND_BB_REACHABLE;
89
+ if (b->successors_count == 0) {
90
+ b->flags |= ZEND_BB_EXIT;
91
+ return;
92
+ }
93
+
94
+ for (i = 0; i < b->successors_count; i++) {
95
+ zend_basic_block *succ = blocks + b->successors[i];
96
+
97
+ if (b->len != 0) {
98
+ zend_uchar opcode = opcodes[b->start + b->len - 1].opcode;
99
+ if (b->successors_count == 1) {
100
+ if (opcode == ZEND_JMP) {
101
+ succ->flags |= ZEND_BB_TARGET;
102
+ } else {
103
+ succ->flags |= ZEND_BB_FOLLOW;
104
+
105
+ if ((cfg->flags & ZEND_CFG_STACKLESS)) {
106
+ if (opcode == ZEND_INCLUDE_OR_EVAL ||
107
+ opcode == ZEND_GENERATOR_CREATE ||
108
+ opcode == ZEND_YIELD ||
109
+ opcode == ZEND_YIELD_FROM ||
110
+ opcode == ZEND_DO_FCALL ||
111
+ opcode == ZEND_DO_UCALL ||
112
+ opcode == ZEND_DO_FCALL_BY_NAME) {
113
+ succ->flags |= ZEND_BB_ENTRY;
114
+ }
115
+ }
116
+ if ((cfg->flags & ZEND_CFG_RECV_ENTRY)) {
117
+ if (opcode == ZEND_RECV ||
118
+ opcode == ZEND_RECV_INIT) {
119
+ succ->flags |= ZEND_BB_RECV_ENTRY;
120
+ }
121
+ }
122
+ }
123
+ } else if (b->successors_count == 2) {
124
+ if (i == 0 || opcode == ZEND_JMPZNZ) {
125
+ succ->flags |= ZEND_BB_TARGET;
126
+ } else {
127
+ succ->flags |= ZEND_BB_FOLLOW;
128
+ }
129
+ } else {
130
+ ZEND_ASSERT(opcode == ZEND_SWITCH_LONG || opcode == ZEND_SWITCH_STRING);
131
+ if (i == b->successors_count - 1) {
132
+ succ->flags |= ZEND_BB_FOLLOW | ZEND_BB_TARGET;
133
+ } else {
134
+ succ->flags |= ZEND_BB_TARGET;
135
+ }
136
+ }
137
+ } else {
138
+ succ->flags |= ZEND_BB_FOLLOW;
139
+ }
140
+
141
+ if (i == b->successors_count - 1) {
142
+ /* Tail call optimization */
143
+ if (succ->flags & ZEND_BB_REACHABLE) {
144
+ return;
145
+ }
146
+
147
+ b = succ;
148
+ break;
149
+ } else {
150
+ /* Recusively check reachability */
151
+ if (!(succ->flags & ZEND_BB_REACHABLE)) {
152
+ zend_mark_reachable(opcodes, cfg, succ);
153
+ }
154
+ }
155
+ }
156
+ }
157
+ }
158
+ /* }}} */
159
+
160
+ static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg, int start) /* {{{ */
161
+ {
162
+ zend_basic_block *blocks = cfg->blocks;
163
+
164
+ blocks[start].flags = ZEND_BB_START;
165
+ zend_mark_reachable(op_array->opcodes, cfg, blocks + start);
166
+
167
+ if (op_array->last_try_catch) {
168
+ zend_basic_block *b;
169
+ int j, changed;
170
+ uint32_t *block_map = cfg->map;
171
+
172
+ do {
173
+ changed = 0;
174
+
175
+ /* Add exception paths */
176
+ for (j = 0; j < op_array->last_try_catch; j++) {
177
+
178
+ /* check for jumps into the middle of try block */
179
+ b = blocks + block_map[op_array->try_catch_array[j].try_op];
180
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
181
+ zend_basic_block *end;
182
+
183
+ if (op_array->try_catch_array[j].catch_op) {
184
+ end = blocks + block_map[op_array->try_catch_array[j].catch_op];
185
+ while (b != end) {
186
+ if (b->flags & ZEND_BB_REACHABLE) {
187
+ op_array->try_catch_array[j].try_op = b->start;
188
+ break;
189
+ }
190
+ b++;
191
+ }
192
+ }
193
+ b = blocks + block_map[op_array->try_catch_array[j].try_op];
194
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
195
+ if (op_array->try_catch_array[j].finally_op) {
196
+ end = blocks + block_map[op_array->try_catch_array[j].finally_op];
197
+ while (b != end) {
198
+ if (b->flags & ZEND_BB_REACHABLE) {
199
+ op_array->try_catch_array[j].try_op = op_array->try_catch_array[j].catch_op;
200
+ changed = 1;
201
+ zend_mark_reachable(op_array->opcodes, cfg, blocks + block_map[op_array->try_catch_array[j].try_op]);
202
+ break;
203
+ }
204
+ b++;
205
+ }
206
+ }
207
+ }
208
+ }
209
+
210
+ b = blocks + block_map[op_array->try_catch_array[j].try_op];
211
+ if (b->flags & ZEND_BB_REACHABLE) {
212
+ b->flags |= ZEND_BB_TRY;
213
+ if (op_array->try_catch_array[j].catch_op) {
214
+ b = blocks + block_map[op_array->try_catch_array[j].catch_op];
215
+ b->flags |= ZEND_BB_CATCH;
216
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
217
+ changed = 1;
218
+ zend_mark_reachable(op_array->opcodes, cfg, b);
219
+ }
220
+ }
221
+ if (op_array->try_catch_array[j].finally_op) {
222
+ b = blocks + block_map[op_array->try_catch_array[j].finally_op];
223
+ b->flags |= ZEND_BB_FINALLY;
224
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
225
+ changed = 1;
226
+ zend_mark_reachable(op_array->opcodes, cfg, b);
227
+ }
228
+ }
229
+ if (op_array->try_catch_array[j].finally_end) {
230
+ b = blocks + block_map[op_array->try_catch_array[j].finally_end];
231
+ b->flags |= ZEND_BB_FINALLY_END;
232
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
233
+ changed = 1;
234
+ zend_mark_reachable(op_array->opcodes, cfg, b);
235
+ }
236
+ }
237
+ } else {
238
+ if (op_array->try_catch_array[j].catch_op) {
239
+ ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].catch_op]].flags & ZEND_BB_REACHABLE));
240
+ }
241
+ if (op_array->try_catch_array[j].finally_op) {
242
+ ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_op]].flags & ZEND_BB_REACHABLE));
243
+ }
244
+ if (op_array->try_catch_array[j].finally_end) {
245
+ ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_end]].flags & ZEND_BB_REACHABLE));
246
+ }
247
+ }
248
+ }
249
+ } while (changed);
250
+ }
251
+ }
252
+ /* }}} */
253
+
254
+ void zend_cfg_remark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
255
+ {
256
+ zend_basic_block *blocks = cfg->blocks;
257
+ int i;
258
+ int start = 0;
259
+
260
+ for (i = 0; i < cfg->blocks_count; i++) {
261
+ if (blocks[i].flags & ZEND_BB_REACHABLE) {
262
+ start = i;
263
+ i++;
264
+ break;
265
+ }
266
+ }
267
+
268
+ /* clear all flags */
269
+ for (i = 0; i < cfg->blocks_count; i++) {
270
+ blocks[i].flags = 0;
271
+ }
272
+
273
+ zend_mark_reachable_blocks(op_array, cfg, start);
274
+ }
275
+ /* }}} */
276
+
277
+ static void initialize_block(zend_basic_block *block) {
278
+ block->flags = 0;
279
+ block->successors = block->successors_storage;
280
+ block->successors_count = 0;
281
+ block->predecessors_count = 0;
282
+ block->predecessor_offset = -1;
283
+ block->idom = -1;
284
+ block->loop_header = -1;
285
+ block->level = -1;
286
+ block->children = -1;
287
+ block->next_child = -1;
288
+ }
289
+
290
+ #define BB_START(i) do { \
291
+ if (!block_map[i]) { blocks_count++;} \
292
+ block_map[i]++; \
293
+ } while (0)
294
+
295
+ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg) /* {{{ */
296
+ {
297
+ uint32_t flags = 0;
298
+ uint32_t i;
299
+ int j;
300
+ uint32_t *block_map;
301
+ zend_function *fn;
302
+ int blocks_count = 0;
303
+ zend_basic_block *blocks;
304
+ zval *zv;
305
+ zend_bool extra_entry_block = 0;
306
+
307
+ cfg->flags = build_flags & (ZEND_CFG_STACKLESS|ZEND_CFG_RECV_ENTRY);
308
+
309
+ cfg->map = block_map = zend_arena_calloc(arena, op_array->last, sizeof(uint32_t));
310
+
311
+ /* Build CFG, Step 1: Find basic blocks starts, calculate number of blocks */
312
+ BB_START(0);
313
+ for (i = 0; i < op_array->last; i++) {
314
+ zend_op *opline = op_array->opcodes + i;
315
+ switch (opline->opcode) {
316
+ case ZEND_RECV:
317
+ case ZEND_RECV_INIT:
318
+ if (build_flags & ZEND_CFG_RECV_ENTRY) {
319
+ BB_START(i + 1);
320
+ }
321
+ break;
322
+ case ZEND_RETURN:
323
+ case ZEND_RETURN_BY_REF:
324
+ case ZEND_GENERATOR_RETURN:
325
+ case ZEND_EXIT:
326
+ case ZEND_THROW:
327
+ if (i + 1 < op_array->last) {
328
+ BB_START(i + 1);
329
+ }
330
+ break;
331
+ case ZEND_INCLUDE_OR_EVAL:
332
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
333
+ case ZEND_GENERATOR_CREATE:
334
+ case ZEND_YIELD:
335
+ case ZEND_YIELD_FROM:
336
+ if (build_flags & ZEND_CFG_STACKLESS) {
337
+ BB_START(i + 1);
338
+ }
339
+ break;
340
+ case ZEND_DO_FCALL:
341
+ case ZEND_DO_UCALL:
342
+ case ZEND_DO_FCALL_BY_NAME:
343
+ flags |= ZEND_FUNC_HAS_CALLS;
344
+ if (build_flags & ZEND_CFG_STACKLESS) {
345
+ BB_START(i + 1);
346
+ }
347
+ break;
348
+ case ZEND_DO_ICALL:
349
+ flags |= ZEND_FUNC_HAS_CALLS;
350
+ break;
351
+ case ZEND_INIT_FCALL:
352
+ case ZEND_INIT_NS_FCALL_BY_NAME:
353
+ zv = CRT_CONSTANT(opline->op2);
354
+ if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
355
+ /* The third literal is the lowercased unqualified name */
356
+ zv += 2;
357
+ }
358
+ if ((fn = zend_hash_find_ptr(EG(function_table), Z_STR_P(zv))) != NULL) {
359
+ if (fn->type == ZEND_INTERNAL_FUNCTION) {
360
+ flags |= zend_cfg_classify_function(
361
+ Z_STR_P(zv), opline->extended_value);
362
+ }
363
+ }
364
+ break;
365
+ case ZEND_FAST_CALL:
366
+ BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
367
+ BB_START(i + 1);
368
+ break;
369
+ case ZEND_FAST_RET:
370
+ if (i + 1 < op_array->last) {
371
+ BB_START(i + 1);
372
+ }
373
+ break;
374
+ case ZEND_JMP:
375
+ BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
376
+ if (i + 1 < op_array->last) {
377
+ BB_START(i + 1);
378
+ }
379
+ break;
380
+ case ZEND_JMPZNZ:
381
+ BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
382
+ BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
383
+ if (i + 1 < op_array->last) {
384
+ BB_START(i + 1);
385
+ }
386
+ break;
387
+ case ZEND_JMPZ:
388
+ case ZEND_JMPNZ:
389
+ case ZEND_JMPZ_EX:
390
+ case ZEND_JMPNZ_EX:
391
+ case ZEND_JMP_SET:
392
+ case ZEND_COALESCE:
393
+ case ZEND_ASSERT_CHECK:
394
+ BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
395
+ BB_START(i + 1);
396
+ break;
397
+ case ZEND_CATCH:
398
+ if (!(opline->extended_value & ZEND_LAST_CATCH)) {
399
+ BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
400
+ }
401
+ BB_START(i + 1);
402
+ break;
403
+ case ZEND_FE_FETCH_R:
404
+ case ZEND_FE_FETCH_RW:
405
+ BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
406
+ BB_START(i + 1);
407
+ break;
408
+ case ZEND_FE_RESET_R:
409
+ case ZEND_FE_RESET_RW:
410
+ BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
411
+ BB_START(i + 1);
412
+ break;
413
+ case ZEND_SWITCH_LONG:
414
+ case ZEND_SWITCH_STRING:
415
+ {
416
+ HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2));
417
+ zval *zv;
418
+ ZEND_HASH_FOREACH_VAL(jumptable, zv) {
419
+ BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)));
420
+ } ZEND_HASH_FOREACH_END();
421
+ BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
422
+ BB_START(i + 1);
423
+ break;
424
+ }
425
+ case ZEND_FETCH_R:
426
+ case ZEND_FETCH_W:
427
+ case ZEND_FETCH_RW:
428
+ case ZEND_FETCH_FUNC_ARG:
429
+ case ZEND_FETCH_IS:
430
+ case ZEND_FETCH_UNSET:
431
+ case ZEND_UNSET_VAR:
432
+ case ZEND_ISSET_ISEMPTY_VAR:
433
+ if (opline->extended_value & ZEND_FETCH_LOCAL) {
434
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
435
+ } else if ((opline->extended_value & (ZEND_FETCH_GLOBAL | ZEND_FETCH_GLOBAL_LOCK)) &&
436
+ !op_array->function_name) {
437
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
438
+ }
439
+ break;
440
+ case ZEND_FUNC_GET_ARGS:
441
+ flags |= ZEND_FUNC_VARARG;
442
+ break;
443
+ case ZEND_EXT_NOP:
444
+ case ZEND_EXT_STMT:
445
+ flags |= ZEND_FUNC_HAS_EXTENDED_STMT;
446
+ break;
447
+ case ZEND_EXT_FCALL_BEGIN:
448
+ case ZEND_EXT_FCALL_END:
449
+ flags |= ZEND_FUNC_HAS_EXTENDED_FCALL;
450
+ break;
451
+ case ZEND_FREE:
452
+ if (opline->extended_value == ZEND_FREE_SWITCH) {
453
+ flags |= ZEND_FUNC_FREE_LOOP_VAR;
454
+ }
455
+ break;
456
+ case ZEND_FE_FREE:
457
+ flags |= ZEND_FUNC_FREE_LOOP_VAR;
458
+ break;
459
+ }
460
+ }
461
+
462
+ /* If the entry block has predecessors, we may need to split it */
463
+ if ((build_flags & ZEND_CFG_NO_ENTRY_PREDECESSORS)
464
+ && op_array->last > 0 && block_map[0] > 1) {
465
+ extra_entry_block = 1;
466
+ }
467
+
468
+ if (op_array->last_try_catch) {
469
+ for (j = 0; j < op_array->last_try_catch; j++) {
470
+ BB_START(op_array->try_catch_array[j].try_op);
471
+ if (op_array->try_catch_array[j].catch_op) {
472
+ BB_START(op_array->try_catch_array[j].catch_op);
473
+ }
474
+ if (op_array->try_catch_array[j].finally_op) {
475
+ BB_START(op_array->try_catch_array[j].finally_op);
476
+ }
477
+ if (op_array->try_catch_array[j].finally_end) {
478
+ BB_START(op_array->try_catch_array[j].finally_end);
479
+ }
480
+ }
481
+ }
482
+
483
+ blocks_count += extra_entry_block;
484
+ cfg->blocks_count = blocks_count;
485
+
486
+ /* Build CFG, Step 2: Build Array of Basic Blocks */
487
+ cfg->blocks = blocks = zend_arena_calloc(arena, sizeof(zend_basic_block), blocks_count);
488
+
489
+ blocks_count = -1;
490
+
491
+ if (extra_entry_block) {
492
+ initialize_block(&blocks[0]);
493
+ blocks[0].start = 0;
494
+ blocks[0].len = 0;
495
+ blocks_count++;
496
+ }
497
+
498
+ for (i = 0; i < op_array->last; i++) {
499
+ if (block_map[i]) {
500
+ if (blocks_count >= 0) {
501
+ blocks[blocks_count].len = i - blocks[blocks_count].start;
502
+ }
503
+ blocks_count++;
504
+ initialize_block(&blocks[blocks_count]);
505
+ blocks[blocks_count].start = i;
506
+ }
507
+ block_map[i] = blocks_count;
508
+ }
509
+
510
+ blocks[blocks_count].len = i - blocks[blocks_count].start;
511
+ blocks_count++;
512
+
513
+ /* Build CFG, Step 3: Calculate successors */
514
+ for (j = 0; j < blocks_count; j++) {
515
+ zend_basic_block *block = &blocks[j];
516
+ zend_op *opline;
517
+ if (block->len == 0) {
518
+ block->successors_count = 1;
519
+ block->successors[0] = j + 1;
520
+ continue;
521
+ }
522
+
523
+ opline = op_array->opcodes + block->start + block->len - 1;
524
+ switch (opline->opcode) {
525
+ case ZEND_FAST_RET:
526
+ case ZEND_RETURN:
527
+ case ZEND_RETURN_BY_REF:
528
+ case ZEND_GENERATOR_RETURN:
529
+ case ZEND_EXIT:
530
+ case ZEND_THROW:
531
+ break;
532
+ case ZEND_JMP:
533
+ block->successors_count = 1;
534
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes];
535
+ break;
536
+ case ZEND_JMPZNZ:
537
+ block->successors_count = 2;
538
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
539
+ block->successors[1] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
540
+ break;
541
+ case ZEND_JMPZ:
542
+ case ZEND_JMPNZ:
543
+ case ZEND_JMPZ_EX:
544
+ case ZEND_JMPNZ_EX:
545
+ case ZEND_JMP_SET:
546
+ case ZEND_COALESCE:
547
+ case ZEND_ASSERT_CHECK:
548
+ block->successors_count = 2;
549
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
550
+ block->successors[1] = j + 1;
551
+ break;
552
+ case ZEND_CATCH:
553
+ if (!(opline->extended_value & ZEND_LAST_CATCH)) {
554
+ block->successors_count = 2;
555
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
556
+ block->successors[1] = j + 1;
557
+ } else {
558
+ block->successors_count = 1;
559
+ block->successors[0] = j + 1;
560
+ }
561
+ break;
562
+ case ZEND_FE_FETCH_R:
563
+ case ZEND_FE_FETCH_RW:
564
+ block->successors_count = 2;
565
+ block->successors[0] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
566
+ block->successors[1] = j + 1;
567
+ break;
568
+ case ZEND_FE_RESET_R:
569
+ case ZEND_FE_RESET_RW:
570
+ block->successors_count = 2;
571
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
572
+ block->successors[1] = j + 1;
573
+ break;
574
+ case ZEND_FAST_CALL:
575
+ block->successors_count = 2;
576
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes];
577
+ block->successors[1] = j + 1;
578
+ break;
579
+ case ZEND_SWITCH_LONG:
580
+ case ZEND_SWITCH_STRING:
581
+ {
582
+ HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2));
583
+ zval *zv;
584
+ uint32_t s = 0;
585
+
586
+ block->successors_count = 2 + zend_hash_num_elements(jumptable);
587
+ block->successors = zend_arena_calloc(arena, block->successors_count, sizeof(int));
588
+
589
+ ZEND_HASH_FOREACH_VAL(jumptable, zv) {
590
+ block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))];
591
+ } ZEND_HASH_FOREACH_END();
592
+
593
+ block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
594
+ block->successors[s++] = j + 1;
595
+ break;
596
+ }
597
+ default:
598
+ block->successors_count = 1;
599
+ block->successors[0] = j + 1;
600
+ break;
601
+ }
602
+ }
603
+
604
+ /* Build CFG, Step 4, Mark Reachable Basic Blocks */
605
+ cfg->flags |= flags;
606
+ zend_mark_reachable_blocks(op_array, cfg, 0);
607
+
608
+ return SUCCESS;
609
+ }
610
+ /* }}} */
611
+
612
+ /* }}} */
pcov-1.0.11/pcov-1.0.11/cfg/704/zend_cfg.h ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ +----------------------------------------------------------------------+
3
+ | Zend Engine, CFG - Control Flow Graph |
4
+ +----------------------------------------------------------------------+
5
+ | Copyright (c) The PHP Group |
6
+ +----------------------------------------------------------------------+
7
+ | This source file is subject to version 3.01 of the PHP license, |
8
+ | that is bundled with this package in the file LICENSE, and is |
9
+ | available through the world-wide-web at the following url: |
10
+ | http://www.php.net/license/3_01.txt |
11
+ | If you did not receive a copy of the PHP license and are unable to |
12
+ | obtain it through the world-wide-web, please send a note to |
13
+ | [email protected] so we can mail you a copy immediately. |
14
+ +----------------------------------------------------------------------+
15
+ | Authors: Dmitry Stogov <[email protected]> |
16
+ +----------------------------------------------------------------------+
17
+ */
18
+
19
+ #ifndef ZEND_CFG_H
20
+ #define ZEND_CFG_H
21
+
22
+ /* zend_basic_bloc.flags */
23
+ #define ZEND_BB_START (1<<0) /* fist block */
24
+ #define ZEND_BB_FOLLOW (1<<1) /* follows the next block */
25
+ #define ZEND_BB_TARGET (1<<2) /* jump taget */
26
+ #define ZEND_BB_EXIT (1<<3) /* without successors */
27
+ #define ZEND_BB_ENTRY (1<<4) /* stackless entry */
28
+ #define ZEND_BB_TRY (1<<5) /* start of try block */
29
+ #define ZEND_BB_CATCH (1<<6) /* start of catch block */
30
+ #define ZEND_BB_FINALLY (1<<7) /* start of finally block */
31
+ #define ZEND_BB_FINALLY_END (1<<8) /* end of finally block */
32
+ #define ZEND_BB_UNREACHABLE_FREE (1<<11) /* unreachable loop free */
33
+ #define ZEND_BB_RECV_ENTRY (1<<12) /* RECV entry */
34
+
35
+ #define ZEND_BB_LOOP_HEADER (1<<16)
36
+ #define ZEND_BB_IRREDUCIBLE_LOOP (1<<17)
37
+
38
+ #define ZEND_BB_REACHABLE (1U<<31)
39
+
40
+ #define ZEND_BB_PROTECTED (ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY|ZEND_BB_TRY|ZEND_BB_CATCH|ZEND_BB_FINALLY|ZEND_BB_FINALLY_END|ZEND_BB_UNREACHABLE_FREE)
41
+
42
+ typedef struct _zend_basic_block {
43
+ int *successors; /* successor block indices */
44
+ uint32_t flags;
45
+ uint32_t start; /* first opcode number */
46
+ uint32_t len; /* number of opcodes */
47
+ int successors_count; /* number of successors */
48
+ int predecessors_count; /* number of predecessors */
49
+ int predecessor_offset; /* offset of 1-st predecessor */
50
+ int idom; /* immediate dominator block */
51
+ int loop_header; /* closest loop header, or -1 */
52
+ int level; /* steps away from the entry in the dom. tree */
53
+ int children; /* list of dominated blocks */
54
+ int next_child; /* next dominated block */
55
+ int successors_storage[2]; /* up to 2 successor blocks */
56
+ } zend_basic_block;
57
+
58
+ /*
59
+ +------------+---+---+---+---+---+
60
+ | |OP1|OP2|EXT| 0 | 1 |
61
+ +------------+---+---+---+---+---+
62
+ |JMP |ADR| | |OP1| - |
63
+ |JMPZ | |ADR| |OP2|FOL|
64
+ |JMPNZ | |ADR| |OP2|FOL|
65
+ |JMPZNZ | |ADR|ADR|OP2|EXT|
66
+ |JMPZ_EX | |ADR| |OP2|FOL|
67
+ |JMPNZ_EX | |ADR| |OP2|FOL|
68
+ |JMP_SET | |ADR| |OP2|FOL|
69
+ |COALESCE | |ADR| |OP2|FOL|
70
+ |ASSERT_CHK | |ADR| |OP2|FOL|
71
+ |NEW | |ADR| |OP2|FOL|
72
+ |DCL_ANON* |ADR| | |OP1|FOL|
73
+ |FE_RESET_* | |ADR| |OP2|FOL|
74
+ |FE_FETCH_* | | |ADR|EXT|FOL|
75
+ |CATCH | | |ADR|EXT|FOL|
76
+ |FAST_CALL |ADR| | |OP1|FOL|
77
+ |FAST_RET | | | | - | - |
78
+ |RETURN* | | | | - | - |
79
+ |EXIT | | | | - | - |
80
+ |THROW | | | | - | - |
81
+ |* | | | |FOL| - |
82
+ +------------+---+---+---+---+---+
83
+ */
84
+
85
+ typedef struct _zend_cfg {
86
+ int blocks_count; /* number of basic blocks */
87
+ int edges_count; /* number of edges */
88
+ zend_basic_block *blocks; /* array of basic blocks */
89
+ int *predecessors;
90
+ uint32_t *map;
91
+ uint32_t flags;
92
+ } zend_cfg;
93
+
94
+ /* Build Flags */
95
+ #define ZEND_RT_CONSTANTS (1U<<31)
96
+ #define ZEND_CFG_STACKLESS (1<<30)
97
+ #define ZEND_SSA_DEBUG_LIVENESS (1<<29)
98
+ #define ZEND_SSA_DEBUG_PHI_PLACEMENT (1<<28)
99
+ #define ZEND_SSA_RC_INFERENCE (1<<27)
100
+ #define ZEND_CFG_NO_ENTRY_PREDECESSORS (1<<25)
101
+ #define ZEND_CFG_RECV_ENTRY (1<<24)
102
+ #define ZEND_CALL_TREE (1<<23)
103
+ #define ZEND_SSA_USE_CV_RESULTS (1<<22)
104
+
105
+ #define CRT_CONSTANT_EX(op_array, opline, node, rt_constants) \
106
+ ((rt_constants) ? \
107
+ RT_CONSTANT(opline, (node)) \
108
+ : \
109
+ CT_CONSTANT_EX(op_array, (node).constant) \
110
+ )
111
+
112
+ #define CRT_CONSTANT(node) \
113
+ CRT_CONSTANT_EX(op_array, opline, node, (build_flags & ZEND_RT_CONSTANTS))
114
+
115
+ #define RETURN_VALUE_USED(opline) \
116
+ ((opline)->result_type != IS_UNUSED)
117
+
118
+ BEGIN_EXTERN_C()
119
+
120
+ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg);
121
+ void zend_cfg_remark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg);
122
+ int zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg);
123
+ int zend_cfg_compute_dominators_tree(const zend_op_array *op_array, zend_cfg *cfg);
124
+ int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg);
125
+
126
+ END_EXTERN_C()
127
+
128
+ #endif /* ZEND_CFG_H */
pcov-1.0.11/pcov-1.0.11/cfg/704/zend_worklist.h ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ +----------------------------------------------------------------------+
3
+ | Zend Engine |
4
+ +----------------------------------------------------------------------+
5
+ | Copyright (c) The PHP Group |
6
+ +----------------------------------------------------------------------+
7
+ | This source file is subject to version 3.01 of the PHP license, |
8
+ | that is bundled with this package in the file LICENSE, and is |
9
+ | available through the world-wide-web at the following url: |
10
+ | http://www.php.net/license/3_01.txt |
11
+ | If you did not receive a copy of the PHP license and are unable to |
12
+ | obtain it through the world-wide-web, please send a note to |
13
+ | [email protected] so we can mail you a copy immediately. |
14
+ +----------------------------------------------------------------------+
15
+ | Authors: Andy Wingo <[email protected]> |
16
+ +----------------------------------------------------------------------+
17
+ */
18
+
19
+ #ifndef _ZEND_WORKLIST_H_
20
+ #define _ZEND_WORKLIST_H_
21
+
22
+ #include "zend_arena.h"
23
+ #include "zend_bitset.h"
24
+
25
+ typedef struct _zend_worklist_stack {
26
+ int *buf;
27
+ int len;
28
+ int capacity;
29
+ } zend_worklist_stack;
30
+
31
+ #define ZEND_WORKLIST_STACK_ALLOCA(s, _len, use_heap) do { \
32
+ (s)->buf = (int*)do_alloca(sizeof(int) * _len, use_heap); \
33
+ (s)->len = 0; \
34
+ (s)->capacity = _len; \
35
+ } while (0)
36
+
37
+ #define ZEND_WORKLIST_STACK_FREE_ALLOCA(s, use_heap) \
38
+ free_alloca((s)->buf, use_heap)
39
+
40
+ static inline int zend_worklist_stack_prepare(zend_arena **arena, zend_worklist_stack *stack, int len)
41
+ {
42
+ ZEND_ASSERT(len >= 0);
43
+
44
+ stack->buf = (int*)zend_arena_calloc(arena, sizeof(*stack->buf), len);
45
+ stack->len = 0;
46
+ stack->capacity = len;
47
+
48
+ return SUCCESS;
49
+ }
50
+
51
+ static inline void zend_worklist_stack_push(zend_worklist_stack *stack, int i)
52
+ {
53
+ ZEND_ASSERT(stack->len < stack->capacity);
54
+ stack->buf[stack->len++] = i;
55
+ }
56
+
57
+ static inline int zend_worklist_stack_peek(zend_worklist_stack *stack)
58
+ {
59
+ ZEND_ASSERT(stack->len);
60
+ return stack->buf[stack->len - 1];
61
+ }
62
+
63
+ static inline int zend_worklist_stack_pop(zend_worklist_stack *stack)
64
+ {
65
+ ZEND_ASSERT(stack->len);
66
+ return stack->buf[--stack->len];
67
+ }
68
+
69
+ typedef struct _zend_worklist {
70
+ zend_bitset visited;
71
+ zend_worklist_stack stack;
72
+ } zend_worklist;
73
+
74
+ #define ZEND_WORKLIST_ALLOCA(w, _len, use_heap) do { \
75
+ (w)->stack.buf = (int*)do_alloca(ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len) + sizeof(zend_ulong) * zend_bitset_len(_len), use_heap); \
76
+ (w)->stack.len = 0; \
77
+ (w)->stack.capacity = _len; \
78
+ (w)->visited = (zend_bitset)((char*)(w)->stack.buf + ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len)); \
79
+ memset((w)->visited, 0, sizeof(zend_ulong) * zend_bitset_len(_len)); \
80
+ } while (0)
81
+
82
+ #define ZEND_WORKLIST_FREE_ALLOCA(w, use_heap) \
83
+ free_alloca((w)->stack.buf, use_heap)
84
+
85
+ static inline int zend_worklist_prepare(zend_arena **arena, zend_worklist *worklist, int len)
86
+ {
87
+ ZEND_ASSERT(len >= 0);
88
+ worklist->visited = (zend_bitset)zend_arena_calloc(arena, sizeof(zend_ulong), zend_bitset_len(len));
89
+ return zend_worklist_stack_prepare(arena, &worklist->stack, len);
90
+ }
91
+
92
+ static inline int zend_worklist_len(zend_worklist *worklist)
93
+ {
94
+ return worklist->stack.len;
95
+ }
96
+
97
+ static inline int zend_worklist_push(zend_worklist *worklist, int i)
98
+ {
99
+ ZEND_ASSERT(i >= 0 && i < worklist->stack.capacity);
100
+
101
+ if (zend_bitset_in(worklist->visited, i)) {
102
+ return 0;
103
+ }
104
+
105
+ zend_bitset_incl(worklist->visited, i);
106
+ zend_worklist_stack_push(&worklist->stack, i);
107
+ return 1;
108
+ }
109
+
110
+ static inline int zend_worklist_peek(zend_worklist *worklist)
111
+ {
112
+ return zend_worklist_stack_peek(&worklist->stack);
113
+ }
114
+
115
+ static inline int zend_worklist_pop(zend_worklist *worklist)
116
+ {
117
+ /* Does not clear visited flag */
118
+ return zend_worklist_stack_pop(&worklist->stack);
119
+ }
120
+
121
+ #endif /* _ZEND_WORKLIST_H_ */
pcov-1.0.11/pcov-1.0.11/config.m4 ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ dnl $Id$
2
+ dnl config.m4 for extension pcov
3
+
4
+ PHP_ARG_ENABLE(pcov, whether to enable php coverage support,
5
+ [ --enable-pcov Enable php coverage support])
6
+
7
+ if test "$PHP_PCOV" != "no"; then
8
+ PHP_VERSION=$($PHP_CONFIG --vernum)
9
+
10
+ AC_MSG_CHECKING(PHP version)
11
+
12
+ if test $PHP_VERSION -gt 80099; then
13
+ PHP_NEW_EXTENSION(pcov, pcov.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
14
+ else
15
+ if test $PHP_VERSION -lt 70100; then
16
+ AC_MSG_ERROR([pcov supports PHP 7.1+])
17
+ elif test $PHP_VERSION -lt 70200; then
18
+ AC_MSG_RESULT(7.1)
19
+ PHP_PCOV_CFG_VERSION=701
20
+ elif test $PHP_VERSION -lt 70300; then
21
+ AC_MSG_RESULT(7.2)
22
+ PHP_PCOV_CFG_VERSION=702
23
+ elif test $PHP_VERSION -lt 70400; then
24
+ AC_MSG_RESULT(7.3)
25
+ PHP_PCOV_CFG_VERSION=703
26
+ else
27
+ AC_MSG_RESULT(7.4+)
28
+ PHP_PCOV_CFG_VERSION=704
29
+ fi
30
+
31
+ PHP_NEW_EXTENSION(pcov, pcov.c cfg/$PHP_PCOV_CFG_VERSION/zend_cfg.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
32
+ PHP_ADD_BUILD_DIR($ext_builddir/cfg/$PHP_PCOV_CFG_VERSION, 1)
33
+ PHP_ADD_INCLUDE($ext_srcdir/cfg/$PHP_PCOV_CFG_VERSION)
34
+ fi
35
+ fi
pcov-1.0.11/pcov-1.0.11/config.w32 ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // $Id$
2
+ // vim:ft=javascript
3
+
4
+ ARG_ENABLE("pcov", "enable php coverage support", "no");
5
+
6
+ if (PHP_PCOV != "no") {
7
+ if (PHP_VERSION < 7 || PHP_VERSION == 7 && PHP_MINOR_VERSION < 1) {
8
+ ERROR("pcov supports PHP 7.1+");
9
+ }
10
+
11
+ if (PHP_VERSION > 8 || PHP_VERSION == 8 && PHP_MINOR_VERSION >= 1) {
12
+ EXTENSION("pcov", "pcov.c", PHP_PCOV_SHARED,
13
+ "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
14
+ } else {
15
+ var cfg_dir;
16
+
17
+ if (PHP_VERSION == 7 && PHP_MINOR_VERSION < 4) {
18
+ cfg_dir = PHP_VERSION + "0" + PHP_MINOR_VERSION;
19
+ } else {
20
+ cfg_dir = "704";
21
+ }
22
+
23
+ EXTENSION("pcov", "pcov.c", PHP_PCOV_SHARED,
24
+ "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 /I" +
25
+ configure_module_dirname + "/cfg/" + cfg_dir);
26
+ ADD_SOURCES(
27
+ configure_module_dirname + "/cfg/" + cfg_dir,
28
+ "zend_cfg.c",
29
+ "pcov"
30
+ );
31
+ }
32
+ }
pcov-1.0.11/pcov-1.0.11/pcov.c ADDED
@@ -0,0 +1,909 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ +----------------------------------------------------------------------+
3
+ | Copyright (c) The PHP Group |
4
+ +----------------------------------------------------------------------+
5
+ | This source file is subject to version 3.01 of the PHP license, |
6
+ | that is bundled with this package in the file LICENSE, and is |
7
+ | available through the world-wide-web at the following url: |
8
+ | http://www.php.net/license/3_01.txt |
9
+ | If you did not receive a copy of the PHP license and are unable to |
10
+ | obtain it through the world-wide-web, please send a note to |
11
+ | [email protected] so we can mail you a copy immediately. |
12
+ +----------------------------------------------------------------------+
13
+ | Author: krakjoe |
14
+ +----------------------------------------------------------------------+
15
+ */
16
+
17
+ /* $Id$ */
18
+
19
+ #ifdef HAVE_CONFIG_H
20
+ #include "config.h"
21
+ #endif
22
+
23
+ #include "php.h"
24
+ #include "php_ini.h"
25
+ #include "ext/standard/info.h"
26
+ #include "ext/pcre/php_pcre.h"
27
+
28
+ #include "zend_arena.h"
29
+ #if PHP_VERSION_ID < 80100
30
+ # include "zend_cfg.h"
31
+ # define PHP_PCOV_CFG ZEND_RT_CONSTANTS
32
+ #else
33
+ # include "Zend/Optimizer/zend_cfg.h"
34
+ # define PHP_PCOV_CFG 0
35
+ #endif
36
+ #include "zend_exceptions.h"
37
+ #include "zend_vm.h"
38
+ #include "zend_vm_opcodes.h"
39
+
40
+ #include "php_pcov.h"
41
+
42
+ #define PCOV_FILTER_ALL 0
43
+ #define PCOV_FILTER_INCLUDE 1
44
+ #define PCOV_FILTER_EXCLUDE 2
45
+
46
+ #define PHP_PCOV_UNCOVERED -1
47
+ #define PHP_PCOV_COVERED 1
48
+
49
+ #ifndef GC_ADDREF
50
+ # define GC_ADDREF(g) ++GC_REFCOUNT(g)
51
+ #endif
52
+
53
+ #if PHP_VERSION_ID < 70300
54
+ #define php_pcre_pce_incref(c) (c)->refcount++
55
+ #define php_pcre_pce_decref(c) (c)->refcount--
56
+ #define GC_SET_REFCOUNT(ref, rc) (GC_REFCOUNT(ref) = (rc))
57
+ #endif
58
+
59
+ #define PHP_PCOV_API_ENABLED_GUARD() do { \
60
+ if (!INI_BOOL("pcov.enabled")) { \
61
+ return; \
62
+ } \
63
+ } while (0);
64
+
65
+ static zval php_pcov_uncovered;
66
+ static zval php_pcov_covered;
67
+
68
+ void (*zend_execute_ex_function)(zend_execute_data *execute_data);
69
+ zend_op_array* (*zend_compile_file_function)(zend_file_handle *fh, int type) = NULL;
70
+
71
+ ZEND_DECLARE_MODULE_GLOBALS(pcov)
72
+
73
+ PHP_INI_BEGIN()
74
+ STD_PHP_INI_BOOLEAN(
75
+ "pcov.enabled", "1",
76
+ PHP_INI_SYSTEM, OnUpdateBool,
77
+ ini.enabled, zend_pcov_globals, pcov_globals)
78
+ STD_PHP_INI_ENTRY (
79
+ "pcov.directory", "",
80
+ PHP_INI_SYSTEM | PHP_INI_PERDIR, OnUpdateString,
81
+ ini.directory, zend_pcov_globals, pcov_globals)
82
+ STD_PHP_INI_ENTRY (
83
+ "pcov.exclude", "",
84
+ PHP_INI_SYSTEM | PHP_INI_PERDIR, OnUpdateString,
85
+ ini.exclude, zend_pcov_globals, pcov_globals)
86
+ STD_PHP_INI_ENTRY(
87
+ "pcov.initial.memory", "65336",
88
+ PHP_INI_SYSTEM | PHP_INI_PERDIR, OnUpdateLong,
89
+ ini.memory, zend_pcov_globals, pcov_globals)
90
+ STD_PHP_INI_ENTRY(
91
+ "pcov.initial.files", "64",
92
+ PHP_INI_SYSTEM | PHP_INI_PERDIR, OnUpdateLong,
93
+ ini.files, zend_pcov_globals, pcov_globals)
94
+ PHP_INI_END()
95
+
96
+ static PHP_GINIT_FUNCTION(pcov)
97
+ {
98
+ #if defined(COMPILE_DL_PCOV) && defined(ZTS)
99
+ ZEND_TSRMLS_CACHE_UPDATE();
100
+ #endif
101
+
102
+ ZEND_SECURE_ZERO(pcov_globals, sizeof(zend_pcov_globals));
103
+ }
104
+
105
+ static zend_always_inline zend_bool php_pcov_wants(zend_string *filename) { /* {{{ */
106
+ if (!PCG(directory)) {
107
+ return 1;
108
+ }
109
+
110
+ if (ZSTR_LEN(filename) < ZSTR_LEN(PCG(directory))) {
111
+ return 0;
112
+ }
113
+
114
+ if (zend_hash_exists(&PCG(wants), filename)) {
115
+ return 1;
116
+ }
117
+
118
+ if (zend_hash_exists(&PCG(ignores), filename)) {
119
+ return 0;
120
+ }
121
+
122
+ if (strncmp(
123
+ ZSTR_VAL(filename),
124
+ ZSTR_VAL(PCG(directory)),
125
+ ZSTR_LEN(PCG(directory))) == SUCCESS) {
126
+
127
+ if (PCG(exclude)) {
128
+ zval match;
129
+
130
+ ZVAL_UNDEF(&match);
131
+
132
+ php_pcre_match_impl(
133
+ PCG(exclude),
134
+ #if PHP_VERSION_ID >= 70400
135
+ filename,
136
+ #else
137
+ ZSTR_VAL(filename), ZSTR_LEN(filename),
138
+ #endif
139
+ &match, NULL,
140
+ 0, 0, 0, 0);
141
+
142
+ if (zend_is_true(&match)) {
143
+ zend_hash_add_empty_element(
144
+ &PCG(ignores), filename);
145
+ return 0;
146
+ }
147
+ }
148
+
149
+ zend_hash_add_empty_element(&PCG(wants), filename);
150
+ return 1;
151
+ }
152
+
153
+ zend_hash_add_empty_element(&PCG(ignores), filename);
154
+ return 0;
155
+ } /* }}} */
156
+
157
+ static zend_always_inline zend_bool php_pcov_ignored_opcode(zend_uchar opcode) { /* {{{ */
158
+ return
159
+ opcode == ZEND_NOP ||
160
+ opcode == ZEND_OP_DATA ||
161
+ opcode == ZEND_FE_FREE ||
162
+ opcode == ZEND_FREE ||
163
+ opcode == ZEND_ASSERT_CHECK ||
164
+ opcode == ZEND_VERIFY_RETURN_TYPE ||
165
+ opcode == ZEND_RECV ||
166
+ opcode == ZEND_RECV_INIT ||
167
+ opcode == ZEND_RECV_VARIADIC ||
168
+ opcode == ZEND_SEND_VAL ||
169
+ opcode == ZEND_SEND_VAR_EX ||
170
+ opcode == ZEND_SEND_REF ||
171
+ opcode == ZEND_SEND_UNPACK ||
172
+ opcode == ZEND_DECLARE_CONST ||
173
+ opcode == ZEND_DECLARE_CLASS ||
174
+ #ifdef ZEND_DECLARE_INHERITED_CLASS
175
+ opcode == ZEND_DECLARE_INHERITED_CLASS ||
176
+ opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED ||
177
+ opcode == ZEND_DECLARE_ANON_INHERITED_CLASS ||
178
+ #else
179
+ opcode == ZEND_DECLARE_CLASS_DELAYED ||
180
+ #endif
181
+ opcode == ZEND_DECLARE_FUNCTION ||
182
+ opcode == ZEND_DECLARE_ANON_CLASS ||
183
+ opcode == ZEND_FAST_RET ||
184
+ opcode == ZEND_FAST_CALL ||
185
+ opcode == ZEND_TICKS ||
186
+ opcode == ZEND_EXT_STMT ||
187
+ opcode == ZEND_EXT_FCALL_BEGIN ||
188
+ opcode == ZEND_EXT_FCALL_END ||
189
+ opcode == ZEND_EXT_NOP ||
190
+ #if PHP_VERSION_ID < 70400
191
+ opcode == ZEND_VERIFY_ABSTRACT_CLASS ||
192
+ opcode == ZEND_ADD_TRAIT ||
193
+ opcode == ZEND_BIND_TRAITS ||
194
+ #endif
195
+ opcode == ZEND_BIND_GLOBAL
196
+ ;
197
+ } /* }}} */
198
+
199
+ static zend_always_inline zend_string* php_pcov_interned_string(zend_string *string) { /* {{{ */
200
+ if (ZSTR_IS_INTERNED(string)) {
201
+ return string;
202
+ }
203
+
204
+ return zend_new_interned_string(zend_string_copy(string));
205
+ } /* }}} */
206
+
207
+ static zend_always_inline php_coverage_t* php_pcov_create(zend_execute_data *execute_data) { /* {{{ */
208
+ php_coverage_t *create = (php_coverage_t*) zend_arena_alloc(&PCG(mem), sizeof(php_coverage_t));
209
+
210
+ create->file = php_pcov_interned_string(EX(func)->op_array.filename);
211
+ create->line = EX(opline)->lineno;
212
+ create->next = NULL;
213
+
214
+ zend_hash_add_empty_element(&PCG(waiting), create->file);
215
+
216
+ return create;
217
+ } /* }}} */
218
+
219
+ static zend_always_inline int php_pcov_has(zend_string *filename, uint32_t lineno) { /* {{{ */
220
+ HashTable *table = zend_hash_find_ptr(&PCG(covered), filename);
221
+
222
+ if (UNEXPECTED(!table)) {
223
+ HashTable covering;
224
+
225
+ zend_hash_init(&covering, 64, NULL, NULL, 0);
226
+
227
+ table = zend_hash_add_mem(
228
+ &PCG(covered), filename, &covering, sizeof(HashTable));
229
+
230
+ zend_hash_index_add_empty_element(table, lineno);
231
+ return 0;
232
+ }
233
+
234
+ if (EXPECTED(zend_hash_index_exists(table, lineno))) {
235
+ return 1;
236
+ }
237
+
238
+ zend_hash_index_add_empty_element(table, lineno);
239
+ return 0;
240
+ } /* }}} */
241
+
242
+ static zend_always_inline int php_pcov_trace(zend_execute_data *execute_data) { /* {{{ */
243
+ if (PCG(enabled)) {
244
+ if (php_pcov_wants(EX(func)->op_array.filename) &&
245
+ !php_pcov_ignored_opcode(EX(opline)->opcode) &&
246
+ !php_pcov_has(EX(func)->op_array.filename, EX(opline)->lineno)) {
247
+
248
+ php_coverage_t *coverage = php_pcov_create(execute_data);
249
+
250
+ if (!PCG(start)) {
251
+ PCG(start) = coverage;
252
+ } else {
253
+ *(PCG(next)) = coverage;
254
+ }
255
+
256
+ PCG(next) = &coverage->next;
257
+ }
258
+ }
259
+
260
+ return zend_vm_call_opcode_handler(execute_data);
261
+ } /* }}} */
262
+
263
+ zend_op_array* php_pcov_compile_file(zend_file_handle *fh, int type) { /* {{{ */
264
+ zend_op_array *result = zend_compile_file_function(fh, type), *mem;
265
+
266
+ if (!result || !result->filename || !php_pcov_wants(result->filename)) {
267
+ return result;
268
+ }
269
+
270
+ if (zend_hash_exists(&PCG(files), result->filename)) {
271
+ return result;
272
+ }
273
+
274
+ mem = zend_hash_add_mem(
275
+ &PCG(files),
276
+ result->filename,
277
+ result, sizeof(zend_op_array));
278
+
279
+ #if PHP_VERSION_ID >= 70400
280
+ if (result->refcount) {
281
+ (*result->refcount)++;
282
+ }
283
+ if (result->static_variables) {
284
+ if (!(GC_FLAGS(result->static_variables) & IS_ARRAY_IMMUTABLE)) {
285
+ GC_ADDREF(result->static_variables);
286
+ }
287
+ }
288
+ mem->fn_flags &= ~ZEND_ACC_HEAP_RT_CACHE;
289
+ #else
290
+ (void)mem;
291
+ function_add_ref((zend_function*)result);
292
+ #endif
293
+
294
+ return result;
295
+ } /* }}} */
296
+
297
+ void php_pcov_execute_ex(zend_execute_data *execute_data) { /* {{{ */
298
+ int zrc = 0;
299
+
300
+ while (1) {
301
+ zrc = php_pcov_trace(execute_data);
302
+
303
+ if (zrc != SUCCESS) {
304
+ if (zrc < SUCCESS) {
305
+ return;
306
+ }
307
+ execute_data = EG(current_execute_data);
308
+ }
309
+ }
310
+ } /* }}} */
311
+
312
+ void php_pcov_covered_dtor(zval *zv) { /* {{{ */
313
+ zend_hash_destroy(Z_PTR_P(zv));
314
+ efree(Z_PTR_P(zv));
315
+ } /* }}} */
316
+
317
+ void php_pcov_files_dtor(zval *zv) { /* {{{ */
318
+ destroy_op_array(Z_PTR_P(zv));
319
+ efree(Z_PTR_P(zv));
320
+ } /* }}} */
321
+
322
+ void php_pcov_filename_dtor(zval *zv) { /* {{{ */
323
+ free(Z_PTR_P(zv));
324
+ } /* }}} */
325
+
326
+ /* {{{ PHP_MINIT_FUNCTION
327
+ */
328
+ PHP_MINIT_FUNCTION(pcov)
329
+ {
330
+ REGISTER_NS_LONG_CONSTANT("pcov", "all", PCOV_FILTER_ALL, CONST_CS|CONST_PERSISTENT);
331
+ REGISTER_NS_LONG_CONSTANT("pcov", "inclusive", PCOV_FILTER_INCLUDE, CONST_CS|CONST_PERSISTENT);
332
+ REGISTER_NS_LONG_CONSTANT("pcov", "exclusive", PCOV_FILTER_EXCLUDE, CONST_CS|CONST_PERSISTENT);
333
+
334
+ REGISTER_NS_STRING_CONSTANT("pcov", "version", PHP_PCOV_VERSION, CONST_CS|CONST_PERSISTENT);
335
+
336
+ REGISTER_INI_ENTRIES();
337
+
338
+ if (INI_BOOL("pcov.enabled")) {
339
+ zend_execute_ex_function = zend_execute_ex;
340
+ zend_execute_ex = php_pcov_execute_ex;
341
+ }
342
+
343
+ ZVAL_LONG(&php_pcov_uncovered, PHP_PCOV_UNCOVERED);
344
+ ZVAL_LONG(&php_pcov_covered, PHP_PCOV_COVERED);
345
+
346
+ return SUCCESS;
347
+ }
348
+ /* }}} */
349
+
350
+ /* {{{ PHP_MSHUTDOWN_FUNCTION
351
+ */
352
+ PHP_MSHUTDOWN_FUNCTION(pcov)
353
+ {
354
+ if (INI_BOOL("pcov.enabled")) {
355
+ zend_execute_ex = zend_execute_ex_function;
356
+ }
357
+
358
+ UNREGISTER_INI_ENTRIES();
359
+
360
+ return SUCCESS;
361
+ }
362
+ /* }}} */
363
+
364
+ const char *php_pcov_directory_defaults[] = { /* {{{ */
365
+ "src",
366
+ "lib",
367
+ "app",
368
+ ".",
369
+ NULL
370
+ }; /* }}} */
371
+
372
+ static void php_pcov_setup_directory(char *directory) { /* {{{ */
373
+ char realpath[MAXPATHLEN];
374
+ zend_stat_t statbuf;
375
+
376
+ if (!directory || !*directory) {
377
+ const char** try = php_pcov_directory_defaults;
378
+
379
+ while (*try) {
380
+ if (VCWD_REALPATH(*try, realpath) &&
381
+ VCWD_STAT(realpath, &statbuf) == SUCCESS) {
382
+ directory = realpath;
383
+ break;
384
+ }
385
+ try++;
386
+ }
387
+ } else {
388
+ if (VCWD_REALPATH(directory, realpath) &&
389
+ VCWD_STAT(realpath, &statbuf) == SUCCESS) {
390
+ directory = realpath;
391
+ }
392
+ }
393
+
394
+ PCG(directory) = zend_string_init(directory, strlen(directory), 0);
395
+ } /* }}} */
396
+
397
+ static zend_always_inline void php_pcov_setup_exclude(char *exclude) { /* {{{ */
398
+ zend_string *pattern;
399
+
400
+ if (!exclude || !*exclude) {
401
+ return;
402
+ }
403
+
404
+ pattern = zend_string_init(
405
+ exclude, strlen(exclude), 0);
406
+
407
+ PCG(exclude) = pcre_get_compiled_regex_cache(pattern);
408
+
409
+ if (PCG(exclude)) {
410
+ php_pcre_pce_incref(PCG(exclude));
411
+ }
412
+
413
+ zend_string_release(pattern);
414
+ } /* }}} */
415
+
416
+ /* {{{ PHP_RINIT_FUNCTION
417
+ */
418
+ PHP_RINIT_FUNCTION(pcov)
419
+ {
420
+ #if defined(COMPILE_DL_PCOV) && defined(ZTS)
421
+ ZEND_TSRMLS_CACHE_UPDATE();
422
+ #endif
423
+
424
+ if (!INI_BOOL("pcov.enabled")) {
425
+ return SUCCESS;
426
+ }
427
+
428
+ PCG(mem) = zend_arena_create(INI_INT("pcov.initial.memory"));
429
+
430
+ zend_hash_init(&PCG(files), INI_INT("pcov.initial.files"), NULL, php_pcov_files_dtor, 0);
431
+ zend_hash_init(&PCG(waiting), INI_INT("pcov.initial.files"), NULL, NULL, 0);
432
+ zend_hash_init(&PCG(ignores), INI_INT("pcov.initial.files"), NULL, NULL, 0);
433
+ zend_hash_init(&PCG(wants), INI_INT("pcov.initial.files"), NULL, NULL, 0);
434
+ zend_hash_init(&PCG(discovered), INI_INT("pcov.initial.files"), NULL, ZVAL_PTR_DTOR, 0);
435
+ zend_hash_init(&PCG(covered), INI_INT("pcov.initial.files"), NULL, php_pcov_covered_dtor, 0);
436
+
437
+ php_pcov_setup_directory(INI_STR("pcov.directory"));
438
+ php_pcov_setup_exclude(INI_STR("pcov.exclude"));
439
+
440
+ #ifdef ZEND_COMPILE_NO_JUMPTABLES
441
+ CG(compiler_options) |= ZEND_COMPILE_NO_JUMPTABLES;
442
+ #endif
443
+
444
+ if (!zend_compile_file_function) {
445
+ zend_compile_file_function = zend_compile_file;
446
+ zend_compile_file = php_pcov_compile_file;
447
+ }
448
+
449
+ PCG(start) = NULL;
450
+ PCG(last) = NULL;
451
+ PCG(next) = NULL;
452
+
453
+ return SUCCESS;
454
+ }
455
+ /* }}} */
456
+
457
+ /* {{{ PHP_RSHUTDOWN_FUNCTION
458
+ */
459
+ PHP_RSHUTDOWN_FUNCTION(pcov)
460
+ {
461
+ if (!INI_BOOL("pcov.enabled") || CG(unclean_shutdown)) {
462
+ return SUCCESS;
463
+ }
464
+
465
+ zend_hash_destroy(&PCG(files));
466
+ zend_hash_destroy(&PCG(ignores));
467
+ zend_hash_destroy(&PCG(wants));
468
+ zend_hash_destroy(&PCG(discovered));
469
+ zend_hash_destroy(&PCG(waiting));
470
+ zend_hash_destroy(&PCG(covered));
471
+
472
+ zend_arena_destroy(PCG(mem));
473
+
474
+ if (PCG(directory)) {
475
+ zend_string_release(PCG(directory));
476
+ }
477
+
478
+ if (PCG(exclude)) {
479
+ php_pcre_pce_decref(PCG(exclude));
480
+ }
481
+
482
+ if (zend_compile_file == php_pcov_compile_file) {
483
+ zend_compile_file = zend_compile_file_function;
484
+ zend_compile_file_function = NULL;
485
+ }
486
+
487
+ return SUCCESS;
488
+ }
489
+ /* }}} */
490
+
491
+ /* {{{ PHP_MINFO_FUNCTION
492
+ */
493
+ PHP_MINFO_FUNCTION(pcov)
494
+ {
495
+ char info[64];
496
+ char *directory = INI_STR("pcov.directory");
497
+ char *exclude = INI_STR("pcov.exclude");
498
+
499
+ php_info_print_table_start();
500
+
501
+ php_info_print_table_header(2,
502
+ "PCOV support",
503
+ INI_BOOL("pcov.enabled") ? "Enabled" : "Disabled");
504
+ php_info_print_table_row(2,
505
+ "PCOV version",
506
+ PHP_PCOV_VERSION);
507
+ php_info_print_table_row(2,
508
+ "pcov.directory",
509
+ directory && *directory ? directory : (PCG(directory) ? ZSTR_VAL(PCG(directory)) : "auto"));
510
+ php_info_print_table_row(2,
511
+ "pcov.exclude",
512
+ exclude && *exclude ? exclude : "none" );
513
+
514
+ snprintf(info, sizeof(info),
515
+ ZEND_LONG_FMT " bytes",
516
+ (zend_long) INI_INT("pcov.initial.memory"));
517
+ php_info_print_table_row(2,
518
+ "pcov.initial.memory", info);
519
+
520
+ snprintf(info, sizeof(info),
521
+ ZEND_LONG_FMT,
522
+ (zend_long) INI_INT("pcov.initial.files"));
523
+ php_info_print_table_row(2,
524
+ "pcov.initial.files", info);
525
+
526
+ php_info_print_table_end();
527
+ }
528
+ /* }}} */
529
+
530
+ static zend_always_inline void php_pcov_report(php_coverage_t *coverage, zval *filter) { /* {{{ */
531
+ zval *table;
532
+ zval *hit;
533
+
534
+ if (!coverage) {
535
+ return;
536
+ }
537
+
538
+ do {
539
+ if ((table = zend_hash_find(Z_ARRVAL_P(filter), coverage->file))) {
540
+ if ((hit = zend_hash_index_find(Z_ARRVAL_P(table), coverage->line))) {
541
+ Z_LVAL_P(hit) = PHP_PCOV_COVERED;
542
+ }
543
+ }
544
+ } while ((coverage = coverage->next));
545
+ } /* }}} */
546
+
547
+ static void php_pcov_discover_code(zend_arena **arena, zend_op_array *ops, zval *return_value) { /* {{{ */
548
+ zend_cfg cfg;
549
+ zend_basic_block *block;
550
+ zend_op *limit = ops->opcodes + ops->last;
551
+ int i = 0;
552
+
553
+ if (ops->fn_flags & ZEND_ACC_ABSTRACT) {
554
+ return;
555
+ }
556
+
557
+ memset(&cfg, 0, sizeof(zend_cfg));
558
+
559
+ zend_build_cfg(arena, ops, PHP_PCOV_CFG, &cfg);
560
+
561
+ for (block = cfg.blocks, i = 0; i < cfg.blocks_count; i++, block++) {
562
+ zend_op *opline = ops->opcodes + block->start,
563
+ *end = opline + block->len;
564
+
565
+ if (!(block->flags & ZEND_BB_REACHABLE)) {
566
+ /*
567
+ * Note that, we don't care about unreachable blocks
568
+ * that would be removed by opcache, because it would
569
+ * create different reports depending on configuration
570
+ */
571
+ continue;
572
+ }
573
+
574
+ while(opline < end) {
575
+ if (php_pcov_ignored_opcode(opline->opcode)) {
576
+ opline++;
577
+ continue;
578
+ }
579
+
580
+ if (!zend_hash_index_exists(Z_ARRVAL_P(return_value), opline->lineno)) {
581
+ zend_hash_index_add(
582
+ Z_ARRVAL_P(return_value),
583
+ opline->lineno, &php_pcov_uncovered);
584
+ }
585
+
586
+ if ((opline +0)->opcode == ZEND_NEW &&
587
+ (opline +1)->opcode == ZEND_DO_FCALL) {
588
+ opline++;
589
+ }
590
+
591
+ opline++;
592
+ }
593
+
594
+ if (block == cfg.blocks && opline == limit) {
595
+ /*
596
+ * If the first basic block finishes at the end of the op array
597
+ * then we don't care about subsequent blocks
598
+ */
599
+ break;
600
+ }
601
+ }
602
+
603
+ #if PHP_VERSION_ID >= 80100
604
+ for (uint32_t def = 0; def < ops->num_dynamic_func_defs; def++) {
605
+ php_pcov_discover_code(arena, ops->dynamic_func_defs[def], return_value);
606
+ }
607
+ #endif
608
+ } /* }}} */
609
+
610
+ static void php_pcov_discover_file(zend_string *file, zval *return_value) { /* {{{ */
611
+ zval discovered;
612
+ zend_op_array *ops;
613
+ zval *cache = zend_hash_find(&PCG(discovered), file);
614
+ zend_arena *mem;
615
+
616
+ if (cache) {
617
+ zval uncached;
618
+ ZVAL_DUP(&uncached, cache);
619
+
620
+ zend_hash_update(Z_ARRVAL_P(return_value), file, &uncached);
621
+ return;
622
+ }
623
+
624
+ if (!(ops = zend_hash_find_ptr(&PCG(files), file))) {
625
+ return;
626
+ }
627
+
628
+ array_init(&discovered);
629
+
630
+ mem = zend_arena_create(1024 * 1024);
631
+
632
+ php_pcov_discover_code(&mem, ops, &discovered);
633
+ {
634
+ zend_class_entry *ce;
635
+ zend_op_array *function;
636
+ ZEND_HASH_FOREACH_PTR(EG(class_table), ce) {
637
+ if (ce->type != ZEND_USER_CLASS) {
638
+ continue;
639
+ }
640
+
641
+ ZEND_HASH_FOREACH_PTR(&ce->function_table, function) {
642
+ if (function->type == ZEND_USER_FUNCTION &&
643
+ function->filename &&
644
+ zend_string_equals(file, function->filename)) {
645
+ php_pcov_discover_code(&mem, function, &discovered);
646
+ }
647
+ } ZEND_HASH_FOREACH_END();
648
+ } ZEND_HASH_FOREACH_END();
649
+ }
650
+
651
+ {
652
+ zend_op_array *function;
653
+ ZEND_HASH_FOREACH_PTR(EG(function_table), function) {
654
+ if (function->type == ZEND_USER_FUNCTION &&
655
+ function->filename &&
656
+ zend_string_equals(file, function->filename)) {
657
+ php_pcov_discover_code(&mem, function, &discovered);
658
+ }
659
+ } ZEND_HASH_FOREACH_END();
660
+ }
661
+
662
+ zend_hash_update(&PCG(discovered), file, &discovered);
663
+ zend_arena_destroy(mem);
664
+
665
+ php_pcov_discover_file(file, return_value);
666
+ } /* }}} */
667
+
668
+ static zend_always_inline void php_pcov_clean(HashTable *table) { /* {{{ */
669
+ if (table->nNumUsed) {
670
+ zend_hash_clean(table);
671
+ }
672
+ } /* }}} */
673
+
674
+ /* {{{ array \pcov\collect(int $type = \pcov\all, array $filter = []); */
675
+ PHP_NAMED_FUNCTION(php_pcov_collect)
676
+ {
677
+ zend_long type = PCOV_FILTER_ALL;
678
+ zval *filter = NULL;
679
+
680
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "|la", &type, &filter) != SUCCESS) {
681
+ return;
682
+ }
683
+
684
+ PHP_PCOV_API_ENABLED_GUARD();
685
+
686
+ if (PCOV_FILTER_ALL != type &&
687
+ PCOV_FILTER_INCLUDE != type &&
688
+ PCOV_FILTER_EXCLUDE != type) {
689
+ zend_throw_error(zend_ce_type_error,
690
+ "type must be "
691
+ "\\pcov\\inclusive, "
692
+ "\\pcov\\exclusive, or \\pcov\\all");
693
+ return;
694
+ }
695
+
696
+ array_init(return_value);
697
+
698
+ if (PCG(last) == PCG(next)) {
699
+ return;
700
+ }
701
+
702
+ PCG(last) = PCG(next);
703
+
704
+ switch(type) {
705
+ case PCOV_FILTER_INCLUDE: {
706
+ zval *filtered;
707
+ ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(filter), filtered) {
708
+ if (Z_TYPE_P(filtered) != IS_STRING) {
709
+ continue;
710
+ }
711
+
712
+ php_pcov_discover_file(Z_STR_P(filtered), return_value);
713
+ } ZEND_HASH_FOREACH_END();
714
+ } break;
715
+
716
+ case PCOV_FILTER_EXCLUDE: {
717
+ zend_string *name;
718
+ zval *filtered;
719
+ ZEND_HASH_FOREACH_STR_KEY(&PCG(files), name) {
720
+ ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(filter), filtered) {
721
+ if (Z_TYPE_P(filtered) != IS_STRING) {
722
+ continue;
723
+ }
724
+
725
+ if (zend_string_equals(name, Z_STR_P(filtered))) {
726
+ goto _php_pcov_collect_exclude;
727
+ }
728
+ } ZEND_HASH_FOREACH_END();
729
+ php_pcov_discover_file(name, return_value);
730
+
731
+ _php_pcov_collect_exclude:
732
+ continue;
733
+ } ZEND_HASH_FOREACH_END();
734
+ } break;
735
+
736
+ case PCOV_FILTER_ALL: {
737
+ zend_string *name;
738
+ ZEND_HASH_FOREACH_STR_KEY(&PCG(files), name) {
739
+ php_pcov_discover_file(name, return_value);
740
+ } ZEND_HASH_FOREACH_END();
741
+ } break;
742
+ }
743
+
744
+ php_pcov_report(PCG(start), return_value);
745
+ } /* }}} */
746
+
747
+ /* {{{ void \pcov\start(void) */
748
+ PHP_NAMED_FUNCTION(php_pcov_start)
749
+ {
750
+ if (zend_parse_parameters_none() != SUCCESS) {
751
+ return;
752
+ }
753
+
754
+ PHP_PCOV_API_ENABLED_GUARD();
755
+
756
+ PCG(enabled) = 1;
757
+ } /* }}} */
758
+
759
+ /* {{{ void \pcov\stop(void) */
760
+ PHP_NAMED_FUNCTION(php_pcov_stop)
761
+ {
762
+ if (zend_parse_parameters_none() != SUCCESS) {
763
+ return;
764
+ }
765
+
766
+ PHP_PCOV_API_ENABLED_GUARD();
767
+
768
+ PCG(enabled) = 0;
769
+ } /* }}} */
770
+
771
+ /* {{{ void \pcov\clear(bool $files = 0) */
772
+ PHP_NAMED_FUNCTION(php_pcov_clear)
773
+ {
774
+ zend_bool files = 0;
775
+
776
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &files) != SUCCESS) {
777
+ return;
778
+ }
779
+
780
+ PHP_PCOV_API_ENABLED_GUARD();
781
+
782
+ if (files) {
783
+ php_pcov_clean(&PCG(files));
784
+ php_pcov_clean(&PCG(discovered));
785
+ }
786
+
787
+ zend_arena_destroy(PCG(mem));
788
+
789
+ PCG(mem) =
790
+ zend_arena_create(
791
+ INI_INT("pcov.initial.memory"));
792
+
793
+ PCG(start) = NULL;
794
+ PCG(last) = NULL;
795
+ PCG(next) = NULL;
796
+
797
+ php_pcov_clean(&PCG(waiting));
798
+ php_pcov_clean(&PCG(covered));
799
+ } /* }}} */
800
+
801
+ /* {{{ array \pcov\waiting(void) */
802
+ PHP_NAMED_FUNCTION(php_pcov_waiting)
803
+ {
804
+ zend_string *waiting;
805
+
806
+ if (zend_parse_parameters_none() != SUCCESS) {
807
+ return;
808
+ }
809
+
810
+ PHP_PCOV_API_ENABLED_GUARD();
811
+
812
+ array_init(return_value);
813
+
814
+ ZEND_HASH_FOREACH_STR_KEY(&PCG(waiting), waiting) {
815
+ add_next_index_str(
816
+ return_value,
817
+ zend_string_copy(waiting));
818
+ } ZEND_HASH_FOREACH_END();
819
+ } /* }}} */
820
+
821
+ /* {{{ int \pcov\memory(void) */
822
+ PHP_NAMED_FUNCTION(php_pcov_memory)
823
+ {
824
+ zend_arena *arena = PCG(mem);
825
+
826
+ if (zend_parse_parameters_none() != SUCCESS) {
827
+ return;
828
+ }
829
+
830
+ PHP_PCOV_API_ENABLED_GUARD();
831
+
832
+ ZVAL_LONG(return_value, 0);
833
+
834
+ do {
835
+ Z_LVAL_P(return_value) += (arena->end - arena->ptr);
836
+ } while ((arena = arena->prev));
837
+ } /* }}} */
838
+
839
+ /* {{{ */
840
+ ZEND_BEGIN_ARG_INFO_EX(php_pcov_collect_arginfo, 0, 0, 0)
841
+ ZEND_ARG_TYPE_INFO(0, type, IS_LONG, 0)
842
+ ZEND_ARG_TYPE_INFO(0, filter, IS_ARRAY, 0)
843
+ ZEND_END_ARG_INFO() /* }}} */
844
+
845
+ /* {{{ */
846
+ ZEND_BEGIN_ARG_INFO_EX(php_pcov_clear_arginfo, 0, 0, 0)
847
+ ZEND_ARG_TYPE_INFO(0, files, _IS_BOOL, 0)
848
+ ZEND_END_ARG_INFO() /* }}} */
849
+
850
+ /* {{{ */
851
+ ZEND_BEGIN_ARG_INFO_EX(php_pcov_no_arginfo, 0, 0, 0)
852
+ ZEND_END_ARG_INFO() /* }}} */
853
+
854
+ /* {{{ php_pcov_functions[]
855
+ */
856
+ const zend_function_entry php_pcov_functions[] = {
857
+ ZEND_NS_FENTRY("pcov", start, php_pcov_start, php_pcov_no_arginfo, 0)
858
+ ZEND_NS_FENTRY("pcov", stop, php_pcov_stop, php_pcov_no_arginfo, 0)
859
+ ZEND_NS_FENTRY("pcov", collect, php_pcov_collect, php_pcov_collect_arginfo, 0)
860
+ ZEND_NS_FENTRY("pcov", clear, php_pcov_clear, php_pcov_clear_arginfo, 0)
861
+ ZEND_NS_FENTRY("pcov", waiting, php_pcov_waiting, php_pcov_no_arginfo, 0)
862
+ ZEND_NS_FENTRY("pcov", memory, php_pcov_memory, php_pcov_no_arginfo, 0)
863
+ PHP_FE_END
864
+ };
865
+ /* }}} */
866
+
867
+ /* {{{ pcov_module_deps[] */
868
+ static const zend_module_dep pcov_module_deps[] = {
869
+ ZEND_MOD_REQUIRED("pcre")
870
+ ZEND_MOD_END
871
+ }; /* }}} */
872
+
873
+ /* {{{ pcov_module_entry
874
+ */
875
+ zend_module_entry pcov_module_entry = {
876
+ STANDARD_MODULE_HEADER_EX,
877
+ NULL,
878
+ pcov_module_deps,
879
+ "pcov",
880
+ php_pcov_functions,
881
+ PHP_MINIT(pcov),
882
+ PHP_MSHUTDOWN(pcov),
883
+ PHP_RINIT(pcov),
884
+ PHP_RSHUTDOWN(pcov),
885
+ PHP_MINFO(pcov),
886
+ PHP_PCOV_VERSION,
887
+ PHP_MODULE_GLOBALS(pcov),
888
+ PHP_GINIT(pcov),
889
+ NULL,
890
+ NULL,
891
+ STANDARD_MODULE_PROPERTIES_EX
892
+ };
893
+ /* }}} */
894
+
895
+ #ifdef COMPILE_DL_PCOV
896
+ #ifdef ZTS
897
+ ZEND_TSRMLS_CACHE_DEFINE()
898
+ #endif
899
+ ZEND_GET_MODULE(pcov)
900
+ #endif
901
+
902
+ /*
903
+ * Local variables:
904
+ * tab-width: 4
905
+ * c-basic-offset: 4
906
+ * End:
907
+ * vim600: noet sw=4 ts=4 fdm=marker
908
+ * vim<600: noet sw=4 ts=4
909
+ */
pcov-1.0.11/pcov-1.0.11/php_pcov.h ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ +----------------------------------------------------------------------+
3
+ | Copyright (c) The PHP Group |
4
+ +----------------------------------------------------------------------+
5
+ | This source file is subject to version 3.01 of the PHP license, |
6
+ | that is bundled with this package in the file LICENSE, and is |
7
+ | available through the world-wide-web at the following url: |
8
+ | http://www.php.net/license/3_01.txt |
9
+ | If you did not receive a copy of the PHP license and are unable to |
10
+ | obtain it through the world-wide-web, please send a note to |
11
+ | [email protected] so we can mail you a copy immediately. |
12
+ +----------------------------------------------------------------------+
13
+ | Author: krakjoe |
14
+ +----------------------------------------------------------------------+
15
+ */
16
+ /* $Id$ */
17
+
18
+ #ifndef PHP_PCOV_H
19
+ #define PHP_PCOV_H
20
+
21
+ extern zend_module_entry pcov_module_entry;
22
+ #define phpext_pcov_ptr &pcov_module_entry
23
+
24
+ #define PHP_PCOV_VERSION "1.0.11"
25
+
26
+ #ifdef PHP_WIN32
27
+ # define PHP_PCOV_API __declspec(dllexport)
28
+ #elif defined(__GNUC__) && __GNUC__ >= 4
29
+ # define PHP_PCOV_API __attribute__ ((visibility("default")))
30
+ #else
31
+ # define PHP_PCOV_API
32
+ #endif
33
+
34
+ #ifdef ZTS
35
+ #include "TSRM.h"
36
+ #endif
37
+
38
+ typedef struct _php_coverage_t php_coverage_t;
39
+
40
+ struct _php_coverage_t {
41
+ zend_string *file;
42
+ uint32_t line;
43
+ php_coverage_t *next;
44
+ };
45
+
46
+ ZEND_BEGIN_MODULE_GLOBALS(pcov)
47
+ zend_bool enabled;
48
+ zend_arena *mem;
49
+ php_coverage_t *start;
50
+ php_coverage_t **next;
51
+ php_coverage_t **last;
52
+ HashTable waiting;
53
+ HashTable files;
54
+ HashTable ignores;
55
+ HashTable wants;
56
+ HashTable discovered;
57
+ HashTable covered;
58
+ zend_string *directory;
59
+ pcre_cache_entry *exclude;
60
+ struct {
61
+ zend_bool enabled;
62
+ zend_long memory;
63
+ zend_long files;
64
+ char *directory;
65
+ char *exclude;
66
+ } ini;
67
+ ZEND_END_MODULE_GLOBALS(pcov)
68
+
69
+ #define PCG(v) ZEND_MODULE_GLOBALS_ACCESSOR(pcov, v)
70
+
71
+ #if defined(ZTS) && defined(COMPILE_DL_PCOV)
72
+ ZEND_TSRMLS_CACHE_EXTERN()
73
+ #endif
74
+
75
+ #endif /* PHP_PCOV_H */
76
+
77
+ /*
78
+ * Local variables:
79
+ * tab-width: 4
80
+ * c-basic-offset: 4
81
+ * End:
82
+ * vim600: noet sw=4 ts=4 fdm=marker
83
+ * vim<600: noet sw=4 ts=4
84
+ */
pcov-1.0.11/pcov-1.0.11/tests/001.phpt ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --TEST--
2
+ start/stop
3
+ --SKIPIF--
4
+ <?php if (!extension_loaded("pcov")) print "skip"; ?>
5
+ --INI--
6
+ pcov.enabled = 1
7
+ --FILE--
8
+ <?php
9
+ \pcov\start();
10
+ $d = [];
11
+ for ($i = 0; $i < 10; $i++) {
12
+ $d[] = $i * 42;
13
+ }
14
+ \pcov\stop();
15
+
16
+ var_dump(\pcov\collect());
17
+ ?>
18
+ --EXPECTF--
19
+ array(1) {
20
+ ["%s%e001.php"]=>
21
+ array(7) {
22
+ [2]=>
23
+ int(-1)
24
+ [3]=>
25
+ int(1)
26
+ [4]=>
27
+ int(1)
28
+ [5]=>
29
+ int(1)
30
+ [7]=>
31
+ int(1)
32
+ [9]=>
33
+ int(-1)
34
+ [11]=>
35
+ int(-1)
36
+ }
37
+ }
38
+
pcov-1.0.11/pcov-1.0.11/tests/002.phpt ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --TEST--
2
+ clear
3
+ --SKIPIF--
4
+ <?php if (!extension_loaded("pcov")) print "skip"; ?>
5
+ --INI--
6
+ pcov.enabled = 1
7
+ --FILE--
8
+ <?php
9
+ \pcov\start();
10
+ $d = [];
11
+ for ($i = 0; $i < 10; $i++) {
12
+ $d[] = $i * 42;
13
+ }
14
+ \pcov\stop();
15
+
16
+ var_dump(\pcov\collect());
17
+
18
+ \pcov\clear();
19
+
20
+ var_dump(\pcov\collect());
21
+ ?>
22
+ --EXPECTF--
23
+ array(1) {
24
+ ["%s%e002.php"]=>
25
+ array(9) {
26
+ [2]=>
27
+ int(-1)
28
+ [3]=>
29
+ int(1)
30
+ [4]=>
31
+ int(1)
32
+ [5]=>
33
+ int(1)
34
+ [7]=>
35
+ int(1)
36
+ [9]=>
37
+ int(-1)
38
+ [11]=>
39
+ int(-1)
40
+ [13]=>
41
+ int(-1)
42
+ [15]=>
43
+ int(-1)
44
+ }
45
+ }
46
+ array(0) {
47
+ }
48
+
49
+
pcov-1.0.11/pcov-1.0.11/tests/003.phpt ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --TEST--
2
+ \pcov\collect non array filter
3
+ --SKIPIF--
4
+ <?php if (!extension_loaded("pcov")) print "skip"; ?>
5
+ --INI--
6
+ pcov.enabled = 0
7
+ --FILE--
8
+ <?php
9
+ var_dump(\pcov\collect(\pcov\inclusive, ""));
10
+ ?>
11
+ --EXPECTF--
12
+ Fatal error: Uncaught TypeError: %s type array, string given in %s%e003.php:2
13
+ Stack trace:
14
+ #0 %s%e003.php(2): pcov\collect(1, '')
15
+ #1 {main}
16
+ thrown in %s%e003.php on line 2
pcov-1.0.11/pcov-1.0.11/tests/004.phpt ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --TEST--
2
+ \pcov\collect not recognized filter type
3
+ --SKIPIF--
4
+ <?php if (!extension_loaded("pcov")) print "skip"; ?>
5
+ --INI--
6
+ pcov.enabled = 1
7
+ --FILE--
8
+ <?php
9
+ var_dump(\pcov\collect(42));
10
+ ?>
11
+ --EXPECTF--
12
+ Fatal error: Uncaught TypeError: type must be \pcov\inclusive, \pcov\exclusive, or \pcov\all in %s%e004.php:2
13
+ Stack trace:
14
+ #0 %s%e004.php(2): pcov\collect(42)
15
+ #1 {main}
16
+ thrown in %s%e004.php on line 2
17
+
pcov-1.0.11/pcov-1.0.11/tests/005.phpt ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --TEST--
2
+ anonymous class
3
+ --SKIPIF--
4
+ <?php if (!extension_loaded("pcov")) print "skip"; ?>
5
+ --INI--
6
+ pcov.enabled = 1
7
+ --FILE--
8
+ <?php
9
+ \pcov\start();
10
+ $a = new class() {};
11
+ \pcov\stop();
12
+ var_dump(\pcov\collect());
13
+ ?>
14
+ --EXPECTF--
15
+ array(1) {
16
+ ["%s%e005.php"]=>
17
+ array(5) {
18
+ [2]=>
19
+ int(-1)
20
+ [3]=>
21
+ int(1)
22
+ [4]=>
23
+ int(1)
24
+ [5]=>
25
+ int(-1)
26
+ [7]=>
27
+ int(-1)
28
+ }
29
+ }
pcov-1.0.11/pcov-1.0.11/tests/006.phpt ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --TEST--
2
+ phpinfo
3
+ --SKIPIF--
4
+ <?php if (!extension_loaded("pcov")) print "skip"; ?>
5
+ --INI--
6
+ pcov.enabled = 0
7
+ --FILE--
8
+ <?php
9
+ phpinfo(INFO_MODULES);
10
+ ?>
11
+ --EXPECTF--
12
+ %A
13
+ PCOV support => Disabled
14
+ PCOV version => %s
15
+ pcov.directory => auto
16
+ pcov.exclude => none
17
+ pcov.initial.memory => 65336 bytes
18
+ pcov.initial.files => 64
19
+ %A
pcov-1.0.11/pcov-1.0.11/tests/007.phpt ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --TEST--
2
+ anonymous function
3
+ --SKIPIF--
4
+ <?php
5
+ if (!extension_loaded("pcov")) print "skip";
6
+ if (PHP_VERSION_ID < 80000) print "skip only for PHP 8";
7
+ ?>
8
+ --INI--
9
+ pcov.enabled = 1
10
+ --FILE--
11
+ <?php
12
+ \pcov\start();
13
+ $a = function() {
14
+ return 'a';
15
+ };
16
+ $a();
17
+ \pcov\stop();
18
+ var_dump(\pcov\collect());
19
+ ?>
20
+ --EXPECTF--
21
+ array(1) {
22
+ ["%s%e007.php"]=>
23
+ array(7) {
24
+ [2]=>
25
+ int(-1)
26
+ [3]=>
27
+ int(1)
28
+ [6]=>
29
+ int(1)
30
+ [7]=>
31
+ int(1)
32
+ [8]=>
33
+ int(-1)
34
+ [10]=>
35
+ int(-1)
36
+ [4]=>
37
+ int(1)
38
+ }
39
+ }
tests/Feature/DatabaseTest.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Tests\Feature;
4
+
5
+ use Illuminate\Foundation\Testing\RefreshDatabase;
6
+ use Illuminate\Foundation\Testing\WithFaker;
7
+ use Tests\TestCase;
8
+
9
+ use App\Models\ListItem;
10
+
11
+ class DatabaseTest extends TestCase
12
+ {
13
+ use RefreshDatabase;
14
+
15
+ public function test_data_added(): void
16
+ {
17
+ ListItem::factory()->count(3)->create(); // means that we are are creating 3 fake data
18
+ // print(json_encode(ListItem::all()));
19
+ $this->assertDatabaseCount('list_items',3);
20
+ }
21
+
22
+ public function test_data_removed(){
23
+ ListItem::factory()->count(5)->create();
24
+ $allitems = ListItem::all();
25
+
26
+ for($i=0;$i<3;$i++){ // delete 3 items
27
+ $allitems[$i]->delete();
28
+ }
29
+
30
+ $this->assertDatabaseCount("list_items",2);
31
+ }
32
+ }
tests/Feature/TODOListControllerTest.php ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Tests\Feature;
4
+
5
+ use Illuminate\Foundation\Testing\RefreshDatabase;
6
+ use Illuminate\Foundation\Testing\WithFaker;
7
+ use Tests\TestCase;
8
+ use App\Http\Controllers\TODOListController;
9
+ use App\Models\ListItem;
10
+
11
+ class TODOListControllerTest extends TestCase
12
+ {
13
+
14
+ use RefreshDatabase;
15
+
16
+ public function test_index(){
17
+
18
+ $response = $this->get("/");
19
+ // print(json_encode($response));
20
+ $response->assertStatus($response->status(),200);
21
+
22
+ }
23
+ public function test_saveItem(){
24
+
25
+ $this->assertDatabaseCount('list_items',0);
26
+
27
+ $response = $this->call("POST", route("saveItem"),[
28
+ "text"=>"task 1"
29
+ ]);
30
+
31
+ $this->assertDatabaseCount('list_items',1); // Check if item is added to the database
32
+ $this->assertEquals(ListItem::all()->first()->is_complete,0); // to check if the status was is incomplete by default
33
+
34
+ $this->assertEquals($response->status(),302); // status code 302 means redirect
35
+
36
+ }
37
+
38
+ public function test_changeStatus_to_done(){
39
+
40
+ $this->test_saveItem(); // add 1 item
41
+
42
+ $response = $this->call("POST", route("changeStatus",1),[
43
+ "checked"=>"on"
44
+ ]);
45
+
46
+ $this->assertEquals(ListItem::all()->first()->is_complete,1); // to check if the status was changed to completed
47
+
48
+ $this->assertEquals($response->status(),302); // status code 302 means redirect
49
+
50
+ }
51
+
52
+ public function test_changeStatus_to_not_done(){
53
+
54
+ $this->test_changeStatus_to_done(); // get to the point where the status is done
55
+
56
+ $response = $this->call("POST", route("changeStatus",1),[
57
+ ]);
58
+
59
+ $this->assertEquals(ListItem::all()->first()->is_complete,0); // to check if the status was changed to incomplete
60
+
61
+ $this->assertEquals($response->status(),302); // status code 302 means redirect
62
+
63
+ }
64
+
65
+ public function test_deleteItem(){
66
+
67
+ $this->test_saveItem(); // create an item
68
+ $response = $this->call("POST",route("deleteItem"),[
69
+ 'id'=>"1",
70
+ ]);
71
+
72
+ $this->assertDatabaseCount("list_items",0); // check if the item is deleted
73
+ $this->assertEquals($response->status(),302); // status code 302 means redirect
74
+ }
75
+ }