Skip to content

Commit 77c07bb

Browse files
committed
A bunch more refactoring. Fixed short circuit bug that existed for quite a while
1 parent a0e9aaa commit 77c07bb

File tree

15 files changed

+217
-181
lines changed

15 files changed

+217
-181
lines changed

lib/PHPCfg/Parser.php

Lines changed: 31 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -97,21 +97,12 @@ protected function loadHandlers(): void
9797
}
9898
}
9999

100-
/**
101-
* @param string $code
102-
* @param string $fileName
103-
* @returns Script
104-
*/
105-
public function parse($code, $fileName)
100+
public function parse(string $code, string $fileName): Script
106101
{
107102
return $this->parseAst($this->astParser->parse($code), $fileName);
108103
}
109104

110-
/**
111-
* @param array $ast PHP-Parser AST
112-
* @param string $fileName
113-
*/
114-
public function parseAst($ast, $fileName): Script
105+
protected function parseAst(array $ast, string $fileName): Script
115106
{
116107
$this->fileName = $fileName;
117108
$ast = $this->astTraverser->traverse($ast);
@@ -213,49 +204,39 @@ public function parseNode(Node $node): void
213204
throw new RuntimeException('Unknown Node Encountered : ' . $type);
214205
}
215206

216-
public function parseTypeList(array $types): array
207+
public function parseTypeList(?Node ...$types): array
217208
{
218-
$parsedTypes = [];
219-
foreach ($types as $type) {
220-
$parsedTypes[] = $this->parseTypeNode($type);
221-
}
222-
223-
return $parsedTypes;
209+
return array_map([$this, 'parseTypeNode'], $types);
224210
}
225211

226212
public function parseTypeNode(?Node $node): Op\Type
227213
{
228214
if (is_null($node)) {
229215
return new Op\Type\Mixed_();
230216
}
231-
if ($node instanceof Node\Name) {
232-
return new Op\Type\Literal(
233-
$node->name,
234-
$this->mapAttributes($node),
235-
);
236-
}
237-
if ($node instanceof Node\NullableType) {
238-
return new Op\Type\Nullable(
239-
$this->parseTypeNode($node->type),
240-
$this->mapAttributes($node),
241-
);
242-
}
243-
if ($node instanceof Node\UnionType) {
244-
$parsedTypes = [];
245-
foreach ($node->types as $type) {
246-
$parsedTypes[] = $this->parseTypeNode($type);
247-
}
248-
249-
return new Op\Type\Union(
250-
$parsedTypes,
251-
$this->mapAttributes($node),
252-
);
253-
}
254-
if ($node instanceof Node\Identifier) {
255-
return new Op\Type\Literal(
256-
$node->name,
257-
$this->mapAttributes($node),
258-
);
217+
switch ($node->getType()) {
218+
case 'Name':
219+
case 'Name_FullyQualified':
220+
// This is safe since we always run name resolution ahead of time
221+
return new Op\Type\Literal(
222+
$node->name,
223+
$this->mapAttributes($node),
224+
);
225+
case 'NullableType':
226+
return new Op\Type\Nullable(
227+
$this->parseTypeNode($node->type),
228+
$this->mapAttributes($node),
229+
);
230+
case 'UnionType':
231+
return new Op\Type\Union(
232+
$this->parseTypeList(...$node->types),
233+
$this->mapAttributes($node),
234+
);
235+
case 'Identifier':
236+
return new Op\Type\Literal(
237+
$node->name,
238+
$this->mapAttributes($node),
239+
);
259240
}
260241
throw new LogicException("Unknown type node: " . $node->getType());
261242
}
@@ -278,17 +259,16 @@ public function parseExprList(array $expr, $readWrite = self::MODE_NONE): array
278259
return $vars;
279260
}
280261

281-
public function parseExprNode($expr)
262+
public function parseExprNode($expr): ?Operand
282263
{
283264
if (null === $expr) {
284-
return;
265+
return null;
285266
}
286267
if (is_scalar($expr)) {
287268
return new Literal($expr);
288269
}
289270
if (is_array($expr)) {
290271
$list = $this->parseExprList($expr);
291-
292272
return end($list);
293273
}
294274
if ($expr instanceof Node\Arg) {
@@ -297,28 +277,6 @@ public function parseExprNode($expr)
297277
if ($expr instanceof Node\Identifier) {
298278
return new Literal($expr->name);
299279
}
300-
if ($expr instanceof Expr\Variable) {
301-
if (is_scalar($expr->name)) {
302-
if ($expr->name === 'this') {
303-
return new Operand\BoundVariable(
304-
$this->parseExprNode($expr->name),
305-
false,
306-
Operand\BoundVariable::SCOPE_OBJECT,
307-
$this->currentClass,
308-
);
309-
}
310-
311-
return new Variable($this->parseExprNode($expr->name));
312-
}
313-
314-
// variable variable
315-
$this->block->children[] = $op = new Op\Expr\VarVar(
316-
$this->readVariable($this->parseExprNode($expr->name)),
317-
$this->mapAttributes($expr)
318-
);
319-
320-
return $op->result;
321-
}
322280
if ($expr instanceof Node\Name) {
323281
$isReserved = in_array(strtolower($expr->getLast()), ['int', 'string', 'array', 'callable', 'float', 'bool'], true);
324282
if ($isReserved) {
@@ -328,15 +286,11 @@ public function parseExprNode($expr)
328286

329287
return new Literal($expr->toString());
330288
}
331-
if ($expr instanceof Node\Scalar) {
332-
return $this->parseScalarNode($expr);
333-
}
289+
334290
if ($expr instanceof Node\InterpolatedStringPart) {
335291
return new Literal($expr->value);
336292
}
337293

338-
$method = 'parse' . $expr->getType();
339-
340294
if (isset($this->handlers[$expr->getType()])) {
341295
return $this->handlers[$expr->getType()]->handleExpr($expr);
342296
}
@@ -367,27 +321,7 @@ public function parseAttributeGroups(array $attrGroups)
367321
return array_map([$this, 'parseAttributeGroup'], $attrGroups);
368322
}
369323

370-
public function processAssertions(Operand $op, Block $if, Block $else): void
371-
{
372-
$block = $this->block;
373-
foreach ($op->assertions as $assert) {
374-
$this->block = $if;
375-
array_unshift($this->block->children, new Op\Expr\Assertion(
376-
$this->readVariable($assert['var']),
377-
$this->writeVariable($assert['var']),
378-
$this->readAssertion($assert['assertion']),
379-
));
380-
$this->block = $else;
381-
array_unshift($this->block->children, new Op\Expr\Assertion(
382-
$this->readVariable($assert['var']),
383-
$this->writeVariable($assert['var']),
384-
new Assertion\NegatedAssertion([$this->readAssertion($assert['assertion'])]),
385-
));
386-
}
387-
$this->block = $block;
388-
}
389-
390-
protected function readAssertion(Assertion $assert): Assertion
324+
public function readAssertion(Assertion $assert): Assertion
391325
{
392326
if ($assert->value instanceof Operand) {
393327
return new $assert($this->readVariable($assert->value));
@@ -408,46 +342,6 @@ protected function throwUndefinedLabelError(): void
408342
}
409343

410344

411-
412-
413-
private function parseScalarNode(Node\Scalar $scalar): Operand
414-
{
415-
switch ($scalar->getType()) {
416-
case 'Scalar_InterpolatedString':
417-
case 'Scalar_Encapsed':
418-
$op = new Op\Expr\ConcatList($this->parseExprList($scalar->parts, self::MODE_READ), $this->mapAttributes($scalar));
419-
$this->block->children[] = $op;
420-
421-
return $op->result;
422-
case 'Scalar_Float':
423-
case 'Scalar_Int':
424-
case 'Scalar_LNumber':
425-
case 'Scalar_String':
426-
case 'Scalar_InterpolatedStringPart':
427-
case 'Scalar_EncapsedStringPart':
428-
return new Literal($scalar->value);
429-
case 'Scalar_MagicConst_Class':
430-
// TODO
431-
return new Literal('__CLASS__');
432-
case 'Scalar_MagicConst_Dir':
433-
return new Literal(dirname($this->fileName));
434-
case 'Scalar_MagicConst_File':
435-
return new Literal($this->fileName);
436-
case 'Scalar_MagicConst_Namespace':
437-
// TODO
438-
return new Literal('__NAMESPACE__');
439-
case 'Scalar_MagicConst_Method':
440-
// TODO
441-
return new Literal('__METHOD__');
442-
case 'Scalar_MagicConst_Function':
443-
// TODO
444-
return new Literal('__FUNCTION__');
445-
default:
446-
var_dump($scalar);
447-
throw new RuntimeException('Unknown how to deal with scalar type ' . $scalar->getType());
448-
}
449-
}
450-
451345
public function parseParameterList(Func $func, array $params): array
452346
{
453347
if (empty($params)) {

lib/PHPCfg/ParserHandler.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ protected function addOp(Op $op): void
8383
case 'Stmt_JumpIf':
8484
$op->if->addParent($this->parser->block);
8585
$op->else->addParent($this->parser->block);
86+
$this->processAssertions($op->cond, $op->if, $op->else);
8687
break;
8788
case 'Stmt_Jump':
8889
$op->target->addParent($this->parser->block);
@@ -96,4 +97,24 @@ protected function addExpr(Op\Expr $expr): Operand
9697
return $expr->result;
9798
}
9899

100+
protected function processAssertions(Operand $op, Block $if, Block $else): void
101+
{
102+
$block = $this->block();
103+
foreach ($op->assertions as $assert) {
104+
$this->block($if);
105+
array_unshift($this->block()->children, new Op\Expr\Assertion(
106+
$this->parser->readVariable($assert['var']),
107+
$this->parser->writeVariable($assert['var']),
108+
$this->parser->readAssertion($assert['assertion']),
109+
));
110+
$this->block($else);
111+
array_unshift($this->block()->children, new Op\Expr\Assertion(
112+
$this->parser->readVariable($assert['var']),
113+
$this->parser->writeVariable($assert['var']),
114+
new Assertion\NegatedAssertion([$this->parser->readAssertion($assert['assertion'])]),
115+
));
116+
}
117+
$this->block($block);
118+
}
119+
99120
}

lib/PHPCfg/ParserHandler/Batch/BinaryOp.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -87,27 +87,27 @@ private function parseShortCircuiting(AstBinaryOp $expr, $isOr): Operand
8787
{
8888
$result = new Operand\Temporary();
8989
$longBlock = $this->createBlockWithCatchTarget();
90+
$midBlock = $this->createBlockWithCatchTarget();
9091
$endBlock = $this->createBlockWithCatchTarget();
9192

9293
$left = $this->parser->readVariable($this->parser->parseExprNode($expr->left));
93-
$if = $isOr ? $endBlock : $longBlock;
94-
$else = $isOr ? $longBlock : $endBlock;
94+
$if = $isOr ? $midBlock : $longBlock;
95+
$else = $isOr ? $longBlock : $midBlock;
9596

9697
$this->addOp(new Op\Stmt\JumpIf($left, $if, $else));
97-
$longBlock->addParent($this->block());
98-
$endBlock->addParent($this->block());
9998

10099
$this->block($longBlock);
101100
$right = $this->parser->readVariable($this->parser->parseExprNode($expr->right));
102-
$boolCast = new Op\Expr\Cast\Bool_($right);
103-
$this->addOp($boolCast);
101+
$castResult = $this->addExpr(new Op\Expr\Cast\Bool_($right));
102+
$this->addOp(new Op\Stmt\Jump($endBlock));
103+
104+
$this->block($midBlock);
104105
$this->addOp(new Op\Stmt\Jump($endBlock));
105-
$endBlock->addParent($this->block());
106106

107107
$this->block($endBlock);
108108
$phi = new Op\Phi($result, ['block' => $this->block()]);
109109
$phi->addOperand(new Operand\Literal($isOr));
110-
$phi->addOperand($boolCast->result);
110+
$phi->addOperand($castResult);
111111
$this->block()->phi[] = $phi;
112112

113113
$mode = $isOr ? Assertion::MODE_UNION : Assertion::MODE_INTERSECTION;
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
3+
/**
4+
* This file is part of PHP-CFG, a Control flow graph implementation for PHP
5+
*
6+
* @copyright 2015 Anthony Ferrara. All rights reserved
7+
* @license MIT See LICENSE at the root of the project for more info
8+
*/
9+
10+
namespace PHPCfg\ParserHandler\Batch;
11+
12+
use PHPCfg\Op;
13+
use PHPCfg\ParserHandler;
14+
use PHPCfg\Parser;
15+
use PhpParser\Node;
16+
use PHPCfg\Operand;
17+
18+
class Scalar extends ParserHandler
19+
{
20+
private const MAP = [
21+
'Scalar_Encapsed' => true,
22+
'Scalar_Float' => true,
23+
'Scalar_Int' => true,
24+
'Scalar_InterpolatedString' => true,
25+
'Scalar_LNumber' => true,
26+
'Scalar_String' => true,
27+
];
28+
29+
public function isBatch(): bool
30+
{
31+
return true;
32+
}
33+
34+
public function supports(Node $expr): bool
35+
{
36+
return isset(self::MAP[$expr->getType()]) || strpos($expr->getType(), 'Scalar_MagicConst_') === 0;
37+
}
38+
39+
public function handleExpr(Node\Expr $scalar): Operand {
40+
switch ($scalar->getType()) {
41+
case 'Scalar_InterpolatedString':
42+
case 'Scalar_Encapsed':
43+
return $this->addExpr(new Op\Expr\ConcatList(
44+
$this->parser->parseExprList($scalar->parts, Parser::MODE_READ),
45+
$this->mapAttributes($scalar)
46+
));
47+
case 'Scalar_Float':
48+
case 'Scalar_Int':
49+
case 'Scalar_LNumber':
50+
case 'Scalar_String':
51+
case 'Scalar_InterpolatedStringPart':
52+
case 'Scalar_EncapsedStringPart':
53+
return new Operand\Literal($scalar->value);
54+
case 'Scalar_MagicConst_Class':
55+
// TODO
56+
return new Operand\Literal('__CLASS__');
57+
case 'Scalar_MagicConst_Dir':
58+
return new Operand\Literal(dirname($this->fileName));
59+
case 'Scalar_MagicConst_File':
60+
return new Operand\Literal($this->fileName);
61+
case 'Scalar_MagicConst_Namespace':
62+
// TODO
63+
return new Operand\Literal('__NAMESPACE__');
64+
case 'Scalar_MagicConst_Method':
65+
// TODO
66+
return new Operand\Literal('__METHOD__');
67+
case 'Scalar_MagicConst_Function':
68+
// TODO
69+
return new Operand\Literal('__FUNCTION__');
70+
default:
71+
var_dump($scalar);
72+
throw new \RuntimeException('Unknown how to deal with scalar type ' . $scalar->getType());
73+
}
74+
}
75+
}

lib/PHPCfg/ParserHandler/Expr/Ternary.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ public function handleExpr(Expr $expr): Operand
2626
$endBlock = $this->createBlockWithCatchTarget();
2727

2828
$this->addOp(new Op\Stmt\JumpIf($cond, $ifBlock, $elseBlock, $attrs));
29-
$this->parser->processAssertions($cond, $ifBlock, $elseBlock);
3029

3130
$this->block($ifBlock);
3231
$ifVar = new Operand\Temporary();

0 commit comments

Comments
 (0)