Polyfill instanceof

This commit is contained in:
Daniil Gentili 2020-09-01 13:53:03 +02:00
parent 440d8b40f2
commit 434928f417
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
3 changed files with 54 additions and 117 deletions

View File

@ -4,6 +4,7 @@ namespace Phabel\Plugin;
use Phabel\Context;
use Phabel\Plugin;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\Assign;
@ -21,6 +22,20 @@ use PhpParser\Node\Scalar\LNumber;
class NestedExpressionFixer extends Plugin
{
/**
* Recursively extract bottom ArrayDimFetch
*
* @param Node $var
* @return Node
*/
private static function &extractWorkVar(Expr &$var): Expr
{
if ($var instanceof ArrayDimFetch && $var->var instanceof ArrayDimFetch) {
return self::extractWorkVar($var->var);
}
return $var;
}
public function leave(Expr $expr, Context $context): ?Expr
{
/** @var array<string, array<class-string<Expr>, true>> */
@ -32,24 +47,31 @@ class NestedExpressionFixer extends Plugin
/** @var Expr $value */
$value = &$expr->{$key};
if (!isset($types[\get_class($value)])) {
continue;
$workVar = $this->extractWorkVar($value);
if (!isset($types[\get_class($workVar)])) {
continue;
}
}
switch ($class) {
case ArrayDimFetch::class:
case PropertyFetch::class:
case MethodCall::class:
case New_::class:
case Instanceof_::class:
if ($expr instanceof Instanceof_ && $key === 'class') {
return self::callPoly('instanceOf', $expr->expr, $expr->class);
}
$value = self::callPoly('returnMe', $value);
break;
case New_::class:
case ClassConstFetch::class:
// For all the following expressions, wrapping in a ternary breaks return-by-ref
case StaticCall::class:
case StaticPropertyFetch::class:
case FuncCall::class:
$valueCopy = $value;
return new Ternary(
new BooleanOr(
new Assign($value = $context->getVariable(), $value),
new Assign($value = $context->getVariable(), $valueCopy),
new LNumber(1)
),
$expr,
@ -76,4 +98,17 @@ class NestedExpressionFixer extends Plugin
{
return $data;
}
/**
* Check if a is instance of b
*
* @param class-string|object $a
* @param class-string|object $b
*
* @return boolean
*/
public static function instanceOf($a, $b): bool
{
return $a instanceof $b;
}
}

View File

@ -1,109 +0,0 @@
<?php
namespace Phabel\Target\Php70;
use Phabel\Plugin;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\Isset_;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Expr\Yield_;
/**
* Fix certain compound access statements.
*/
class CompoundAccess extends Plugin
{
/**
* Replace non-simple isset calls.
*
* @param Isset_ $node Isset node
*
* @return void
*/
public function enterIsset(Isset_ $node): void
{
foreach ($node->vars as &$var) {
if ($var instanceof ArrayDimFetch
&& $var->var instanceof Expr
&& !($var->var instanceof Variable || $var->var instanceof FuncCall) {
$var->var = self::callPoly('returnMe', $var->var);
}
}
}
/**
* Fix yield array access.
*
* @param ArrayDimFetch $node Node
*
* @return void
*/
public function enterArrayYield(ArrayDimFetch $node): void
{
if (!$node->var instanceof Node\Expr\Yield_) {
return;
}
$node->var = self::callPoly('returnMe', $node->var);
}
/**
* Fix yield array access.
*
* @param Yield_ $node Yield
*
* @return void
*/
public function enterYield(Yield_ $node): void
{
$value = &$node->value;
if ($value instanceof Node\Expr\Variable && $value->name !== "this") {
return;
}
if ($value instanceof Node\Expr\FuncCall ||
$value instanceof Node\Expr\MethodCall ||
$value instanceof Node\Expr\StaticCall ||
$value instanceof Node\Scalar
) {
return;
}
$value = self::callPoly('returnMe', $value);
}
/**
* Replace method call on yielded|cloned|closure object.
*
* @param MethodCall $node Method call
*
* @return void
*/
public function enterMethodCall(MethodCall $node): void
{
$value = &$node->var;
if (!$value instanceof Node\Expr\Clone_ &&
!$value instanceof Node\Expr\Yield_ &&
!$value instanceof Node\Expr\Closure
) {
return;
}
$value = self::callPoly('returnMe', $value);
}
/**
* Returns the data provided.
*
* @param mixed $data Data
*
* @return mixed
*
* @template T
*
* @psalm-param T $data data
*
* @psalm-return T
*/
public static function returnMe($data)
{
return $data;
}
}

View File

@ -93,7 +93,7 @@ function checkSyntax(string $code, int $startFrom = 56): int
/** @var ReflectionClass[] */
$expressions = [];
foreach ($allClasses = ClassFinder::getClassesInNamespace('PhpParser', ClassFinder::RECURSIVE_MODE) as $class) {
foreach (ClassFinder::getClassesInNamespace('PhpParser', ClassFinder::RECURSIVE_MODE) as $class) {
$class = new ReflectionClass($class);
if ($class->isSubclassOf(Expr::class) && !$class->isAbstract()
&& $class->getName() !== PrintableNewAnonClassNode::class
@ -264,12 +264,23 @@ foreach ($instanceArgTypes as $class => $argTypes) {
}
$keys = [];
foreach ($result['main'] as $version) {
$keys = array_merge_recursive($keys, $version);
}
foreach ($result['main'] as $version) {
$keys = array_merge_recursive($keys, $version);
}
foreach ($keys as &$values) {
$values = array_keys($values);
}
var_dump($keys);
\file_put_contents('result.php', '<?php $result = '.\var_export($result, true).";");
$ckeys = array_fill_keys(array_map(fn ($a) => $a->getname(), $expressions), true);
foreach ($result as &$type) {
foreach ($type as &$version) {
foreach ($version as &$class) {
foreach ($class as &$arguments) {
$arguments = array_diff_key($ckeys, $arguments);
}
}
}
}
\file_put_contents('resultReverse.php', '<?php $result = '.\var_export($result, true).";");