Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 88 additions & 87 deletions docs/bootstrap-inventory.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Regenerate: `php script/bootstrap-inventory.php`
|--------|------:|
| PHP files on vm.php path | 299 |
| Source constructs flagged (blockers) | 10 |
| Source constructs flagged (warnings) | 786 |
| Source constructs flagged (warnings) | 787 |

## Compiler CFG gaps (`lib/Compiler.php`)

Expand Down Expand Up @@ -226,7 +226,7 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered:
| `lib/AOT/ProjectGraph.php` | 0 | 4 |
| `lib/Block.php` | 0 | 4 |
| `lib/Cli/PhpcInit.php` | 0 | 1 |
| `lib/Compiler.php` | 0 | 93 |
| `lib/Compiler.php` | 0 | 94 |
| `lib/Doctor.php` | 0 | 1 |
| `lib/Frame.php` | 0 | 1 |
| `lib/Func.php` | 0 | 1 |
Expand Down Expand Up @@ -1503,9 +1503,9 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered:
### `lib/Block.php`

**Warnings** (review for bootstrap subset):
- new Variable (line 202)
- new Frame (line 227)
- new Variable (line 250)
- new Variable (line 216)
- new Frame (line 241)
- new Variable (line 264)
- 11 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler

### `lib/Cli/PhpcInit.php`
Expand All @@ -1518,96 +1518,97 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered:
**Warnings** (review for bootstrap subset):
- new Func\PHP (line 47)
- new Block (line 52)
- new OpCode (line 143)
- new Block (line 152)
- new OpCode (line 165)
- new Operand\Literal (line 176)
- new OpCode (line 178)
- new Variable (line 193)
- new Operand\Temporary (line 194)
- new OpCode (line 225)
- new Operand\Literal (line 237)
- new OpCode (line 239)
- new OpCode (line 254)
- new OpCode (line 262)
- new OpCode (line 282)
- new OpCode (line 287)
- new OpCode (line 320)
- new OpCode (line 329)
- new OpCode (line 426)
- new OpCode (line 433)
- new OpCode (line 447)
- new OpCode (line 453)
- new OpCode (line 464)
- new Block (line 67)
- new OpCode (line 160)
- new Block (line 169)
- new OpCode (line 182)
- new Operand\Literal (line 193)
- new OpCode (line 195)
- new Variable (line 210)
- new Operand\Temporary (line 211)
- new OpCode (line 242)
- new Operand\Literal (line 254)
- new OpCode (line 256)
- new OpCode (line 271)
- new OpCode (line 279)
- new OpCode (line 299)
- new OpCode (line 303)
- new OpCode (line 334)
- new OpCode (line 342)
- new OpCode (line 438)
- new OpCode (line 445)
- new OpCode (line 459)
- new OpCode (line 465)
- new OpCode (line 476)
- new OpCode (line 482)
- new OpCode (line 495)
- new OpCode (line 506)
- new OpCode (line 514)
- new OpCode (line 520)
- new OpCode (line 488)
- new OpCode (line 494)
- new OpCode (line 507)
- new OpCode (line 518)
- new OpCode (line 526)
- new OpCode (line 531)
- new OpCode (line 532)
- new OpCode (line 538)
- new OpCode (line 545)
- new OpCode (line 551)
- new OpCode (line 556)
- new OpCode (line 543)
- new OpCode (line 550)
- new OpCode (line 557)
- new OpCode (line 563)
- new OpCode (line 570)
- new OpCode (line 568)
- new OpCode (line 575)
- new OpCode (line 581)
- new OpCode (line 588)
- new OpCode (line 594)
- new OpCode (line 599)
- new OpCode (line 582)
- new OpCode (line 587)
- new OpCode (line 593)
- new OpCode (line 600)
- new OpCode (line 606)
- new OpCode (line 615)
- new OpCode (line 620)
- new OpCode (line 611)
- new OpCode (line 618)
- new OpCode (line 627)
- new OpCode (line 640)
- new OpCode (line 646)
- new OpCode (line 632)
- new OpCode (line 639)
- new OpCode (line 652)
- new OpCode (line 671)
- new Operand\Literal (line 694)
- new OpCode (line 697)
- new OpCode (line 705)
- new Block (line 720)
- new Block (line 724)
- new OpCode (line 728)
- new Block (line 735)
- new OpCode (line 746)
- new OpCode (line 755)
- new OpCode (line 763)
- new OpCode (line 772)
- new OpCode (line 778)
- new OpCode (line 786)
- new OpCode (line 789)
- new OpCode (line 795)
- new OpCode (line 813)
- new OpCode (line 828)
- new Block (line 847)
- new Block (line 851)
- new Operand\Literal (line 854)
- new OpCode (line 857)
- new OpCode (line 863)
- new Block (line 867)
- new OpCode (line 871)
- new OpCode (line 877)
- new Block (line 961)
- new Block (line 964)
- new OpCode (line 967)
- new OpCode (line 973)
- new OpCode (line 987)
- new Block (line 994)
- new OpCode (line 997)
- new OpCode (line 1007)
- new Temporary (line 1017)
- new Variable (line 1027)
- new Operand\Temporary (line 1029)
- new Variable (line 1122)
- new OpCode (line 1154)
- new OpCode (line 1160)
- new OpCode (line 1164)
- new OpCode (line 1169)
- 36 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler
- new OpCode (line 658)
- new OpCode (line 664)
- new OpCode (line 683)
- new Operand\Literal (line 706)
- new OpCode (line 709)
- new OpCode (line 717)
- new Block (line 732)
- new Block (line 736)
- new OpCode (line 740)
- new Block (line 747)
- new OpCode (line 758)
- new OpCode (line 767)
- new OpCode (line 775)
- new OpCode (line 784)
- new OpCode (line 790)
- new OpCode (line 798)
- new OpCode (line 801)
- new OpCode (line 807)
- new OpCode (line 825)
- new OpCode (line 840)
- new Block (line 859)
- new Block (line 863)
- new Operand\Literal (line 866)
- new OpCode (line 869)
- new OpCode (line 875)
- new Block (line 879)
- new OpCode (line 883)
- new OpCode (line 889)
- new Block (line 973)
- new Block (line 976)
- new OpCode (line 979)
- new OpCode (line 985)
- new OpCode (line 999)
- new Block (line 1006)
- new OpCode (line 1009)
- new OpCode (line 1019)
- new Temporary (line 1029)
- new Variable (line 1039)
- new Operand\Temporary (line 1041)
- new Variable (line 1134)
- new OpCode (line 1166)
- new OpCode (line 1172)
- new OpCode (line 1176)
- new OpCode (line 1181)
- 37 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler
- 1 closure(s)

### `lib/Doctor.php`
Expand Down
24 changes: 19 additions & 5 deletions lib/Block.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,15 +117,20 @@ public function addOpCode(OpCode ...$ops): void {
}

public function findSlot(Operand $op, Frame $frame): ?Variable {
$byName = self::findVariableInParentFrames($op, $frame);
if (null !== $byName) {
return $byName;
}
if (!$this->scope->contains($op)) {
// check PHI vars
if (!is_null($frame->parent)) {
return $frame->parent->block->findSlot($op, $frame->parent);
}

return null;
}
$idx = $this->scope[$op];
return $frame->scope[$idx];

return $frame->scope[$idx] ?? null;
}

public function slotIndexForVariableName(string $name): ?int
Expand All @@ -149,6 +154,9 @@ private static function findVariableInParentFrames(Operand $op, Frame $frame): ?
return null;
}
for ($f = $frame; null !== $f; $f = $f->parent) {
if ('this' === $name && !empty($f->calledArgs)) {
return $f->calledArgs[0];
}
if (null === $f->block) {
continue;
}
Expand All @@ -167,9 +175,15 @@ public function getFrame(Context $context, ?Frame $frame = null): Frame {
$scopeSize = $this->scope->count();
foreach ($this->scope as $op) {
$pos = $this->scope[$op];
if (null !== $frame && 'this' === self::resolveVariableName($op) && !empty($frame->callArgs)) {
$scope[$pos] = $frame->callArgs[0];
continue;
if (null !== $frame && 'this' === self::resolveVariableName($op)) {
if (!empty($frame->callArgs)) {
$scope[$pos] = $frame->callArgs[0];
continue;
}
if (!empty($frame->calledArgs)) {
$scope[$pos] = $frame->calledArgs[0];
continue;
}
}

if (isset($this->constants[$pos])) {
Expand Down
32 changes: 22 additions & 10 deletions lib/Compiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,23 @@ protected function compileCfgBlock(CfgBlock $block, array $params = []): Block {
return $this->seen[$block];
}

/**
* CFG branch target within the current function: inherit parent locals ($this, params).
*/
protected function compileCfgBranch(CfgBlock $block, Block $parent): Block {
if (!$this->seen->contains($block)) {
$this->seen[$block] = $new = new Block($block);
$new->inheritScopeFrom($parent);
$this->compileBlock($new);
} else {
$this->seen[$block]->inheritScopeFrom($parent);
}
$child = $this->seen[$block];
$child->parents[] = $parent;

return $child;
}

protected function compileBlock(Block $block) {
$this->compileOps($block->orig->children, $block);
}
Expand Down Expand Up @@ -280,15 +297,12 @@ protected function compileOp(Op $op, Block $block) {
protected function compileStmt(Op\Stmt $stmt, Block $block) {
if ($stmt instanceof Op\Stmt\Jump) {
$op = new OpCode(OpCode::TYPE_JUMP);
$op->block1 = $this->compileCfgBlock($stmt->target);
$op->block1->parents[] = $block;
$op->block1 = $this->compileCfgBranch($stmt->target, $block);
$block->addOpCode($op);
} elseif ($stmt instanceof Op\Stmt\JumpIf) {
$op = new OpCode(OpCode::TYPE_JUMPIF, $this->compileOperand($stmt->cond, $block, true));
$op->block1 = $this->compileCfgBlock($stmt->if);
$op->block2 = $this->compileCfgBlock($stmt->else);
$op->block1->parents[] = $block;
$op->block2->parents[] = $block;
$op->block1 = $this->compileCfgBranch($stmt->if, $block);
$op->block2 = $this->compileCfgBranch($stmt->else, $block);
$block->addOpCode($op);
} elseif ($stmt instanceof Op\Stmt\Switch_) {
$canBeSwitch = true;
Expand Down Expand Up @@ -322,13 +336,11 @@ protected function compileSwitchStmt(Op\Stmt\Switch_ $switch, Block $block): voi
$op,
$this->compileOperand($case, $block, true)
);
$caseOp->block1 = $this->compileCfgBlock($switch->targets[$key]);
$caseOp->block1->parents[] = $block;
$caseOp->block1 = $this->compileCfgBranch($switch->targets[$key], $block);
$block->addOpCode($caseOp);
}
$defaultOp = new OpCode(OpCode::TYPE_JUMP);
$defaultOp->block1 = $this->compileCfgBlock($switch->default);
$defaultOp->block1->parents[] = $block;
$defaultOp->block1 = $this->compileCfgBranch($switch->default, $block);
$block->addOpCode($defaultOp);
}

Expand Down
23 changes: 23 additions & 0 deletions test/compliance/cases/language/switch_method_call.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
--TEST--
switch case can call private methods on $this (MiniWebApp Router dispatch)
--FILE--
<?php
class R {
public function dispatch(string $route): void {
switch ($route) {
case 'home':
$this->renderHome();
break;
default:
echo "no\n";
}
}

private function renderHome(): void {
echo "home\n";
}
}

(new R())->dispatch('home');
--EXPECT--
home
4 changes: 2 additions & 2 deletions test/unit/PhpcLintProjectTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,16 @@ public function testDirRelativeIncludeIsFollowed(): void
/**
* @see https://github.com/PurHur/php-compiler/issues/462
*/
public function testLintMiniWebAppHasNoDynamicIncludeWarnings(): void
public function testLintMiniWebAppExitsZero(): void
{
$tree = dirname(__DIR__, 2).'/examples/003-MiniWebApp';
$exit = $this->runLint(['--all', $tree]);
$this->assertSame(0, $exit['code'], $exit['stderr']."\n".$exit['stdout']);
$this->assertStringNotContainsString(
'dynamic include/require',
$exit['stderr'],
$exit['stderr']."\n".$exit['stdout']
);
$this->assertStringContainsString('ClassMethod', $exit['stderr']."\n".$exit['stdout']);
}

public function testDynamicIncludeEmitsWarning(): void
Expand Down
Loading