-
Notifications
You must be signed in to change notification settings - Fork 34
Expand file tree
/
Copy pathAnalyzer.php
More file actions
executable file
·135 lines (123 loc) · 4.62 KB
/
Analyzer.php
File metadata and controls
executable file
·135 lines (123 loc) · 4.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
<?php
declare(strict_types=1);
/**
* This file is part of PHP-Compiler, a PHP CFG Compiler for PHP code
*
* @copyright 2015 Anthony Ferrara. All rights reserved
* @license MIT See LICENSE at the root of the project for more info
*/
namespace PHPCompiler;
use PHPCfg\Op;
use PHPCfg\Operand;
use PHPTypes\Type;
use SplObjectStorage;
class Analyzer
{
public function needsBoundsCheck(Variable $var, Operand $dimOp): bool
{
if ($dimOp instanceof Operand\Literal) {
return false;
}
if (count($dimOp->ops) !== 1) {
return true;
}
if ($dimOp->ops[0] instanceof Op\Expr\BinaryOp\Mod) {
// validate that the right side is <= var->nextFreeElement
if ($dimOp->ops[0]->right instanceof Operand\Literal && $dimOp->ops[0]->right->type->type === Type::TYPE_LONG) {
return $dimOp->ops[0]->right->value > $var->nextFreeElement;
}
}
return true;
}
public function canEscape(Operand $operand, ?SplObjectStorage $seen = null): bool
{
if (null === $seen) {
$seen = new SplObjectStorage();
} elseif ($seen->contains($operand)) {
return false;
}
$seen->attach($operand);
foreach ($operand->usages as $usage) {
if ($usage instanceof Op\Expr\Assign) {
if ($this->canEscape($usage->var, $seen) || $this->canEscape($usage->result, $seen)) {
return true;
}
} elseif ($usage instanceof Op\Expr\ArrayDimFetch || $usage instanceof Op\Phi) {
continue;
} else {
throw new \LogicException('Not implemented escape operand '.get_class($usage));
}
}
return false;
}
public function hasDynamicArrayAppend(Operand $operand, int $size, ?SplObjectStorage $seen = null): bool
{
if (null === $seen) {
$seen = new SplObjectStorage();
} elseif ($seen->contains($operand)) {
return false;
}
$seen->attach($operand);
foreach ($operand->usages as $usage) {
if ($usage instanceof Op\Expr\Assign) {
if ($this->hasDynamicArrayAppend($usage->var, $size, $seen) || $this->hasDynamicArrayAppend($usage->result, $size, $seen)) {
return true;
}
} elseif ($usage instanceof Op\Expr\ArrayDimFetch) {
if (null !== $usage->dim) {
if (! $usage->dim instanceof Operand\Literal) {
if (count($usage->result->ops) > 1) {
// this means that it's a write, disallow it
return true;
}
} elseif ($usage->dim->type->type !== Type::TYPE_LONG) {
return true;
} elseif ($usage->dim->value >= $size) {
return true;
}
} else {
return true;
}
} elseif ($usage instanceof Op\Phi) {
// unsure what to do here skip for now
} else {
throw new \LogicException('Not implemented dynamic append operand '.get_class($usage));
}
}
return false;
}
public function computeStaticArraySize(Operand $operand, ?SplObjectStorage $seen = null): ?int
{
if (null === $seen) {
$seen = new SplObjectStorage();
} elseif ($seen->contains($operand)) {
return null;
}
$seen->attach($operand);
$size = 0;
foreach ($operand->ops as $op) {
if ($op instanceof Op\Expr\Array_) {
$newSize = 0;
foreach ($op->keys as $key) {
if ($key instanceof Operand\NullOperand) {
++$newSize;
} elseif (! $key instanceof Operand\Literal || $key->type->type !== Type::TYPE_LONG) {
return null;
} elseif ($key->value >= $newSize) {
$newSize = $key->value + 1;
}
}
$size = max($size, $newSize);
} elseif ($op instanceof Op\Expr\Assign) {
$newSize = $this->computeStaticArraySize($op->expr, $seen);
if (null === $newSize) {
return null;
}
$size = max($size, $newSize);
} else {
throw new \LogicException('Unknown array write op: '.get_class($op));
}
}
return $size;
}
}