private function parseShortCircuiting(AstBinaryOp $expr, $isOr) { $result = new Temporary(); $longBlock = new Block(); $endBlock = new Block(); $left = $this->readVariable($this->parseExprNode($expr->left)); $if = $isOr ? $endBlock : $longBlock; $else = $isOr ? $longBlock : $endBlock; $this->block->children[] = new JumpIf($left, $if, $else); $longBlock->addParent($this->block); $endBlock->addParent($this->block); $this->block = $longBlock; $right = $this->readVariable($this->parseExprNode($expr->right)); $boolCast = new Op\Expr\Cast\Bool_($right); $this->block->children[] = $boolCast; $this->block->children[] = new Jump($endBlock); $endBlock->addParent($this->block); $this->block = $endBlock; $phi = new Op\Phi($result, ['block' => $this->block]); $phi->addOperand(new Literal($isOr)); $phi->addOperand($boolCast->result); $this->block->phi[] = $phi; $mode = $isOr ? Assertion::MODE_UNION : Assertion::MODE_INTERSECTION; foreach ($left->assertions as $assert) { $result->addAssertion($assert['var'], $assert['assertion'], $mode); } foreach ($right->assertions as $assert) { $result->addAssertion($assert['var'], $assert['assertion'], $mode); } return $result; }
protected function parseExprNode($expr) { if (is_null($expr)) { return null; } elseif (is_scalar($expr)) { return new Literal($expr); } elseif (is_array($expr)) { $list = $this->parseExprList($expr); return end($list); } elseif ($expr instanceof Node\Expr\Variable) { return new Variable($this->parseExprNode($expr->name)); } elseif ($expr instanceof Node\Name) { if (isset($expr->namespacedName)) { return new Literal($expr->namespacedName->toString()); } return new Literal($expr->toString()); } elseif ($expr instanceof Node\Scalar) { return $this->parseScalarNode($expr); } elseif ($expr instanceof Node\Expr\AssignOp) { $v = $this->parseExprNode($expr->var); $e = $this->parseExprNode($expr->expr); $class = ["Expr_AssignOp_BitwiseAnd" => Op\Expr\AssignOp\BitwiseAnd::class, "Expr_AssignOp_BitwiseOr" => Op\Expr\AssignOp\BitwiseOr::class, "Expr_AssignOp_BitwiseXor" => Op\Expr\AssignOp\BitwiseXor::class, "Expr_AssignOp_Concat" => Op\Expr\AssignOp\Concat::class, "Expr_AssignOp_Div" => Op\Expr\AssignOp\Div::class, "Expr_AssignOp_Minus" => Op\Expr\AssignOp\Minus::class, "Expr_AssignOp_Mod" => Op\Expr\AssignOp\Mod::class, "Expr_AssignOp_Mul" => Op\Expr\AssignOp\Mul::class, "Expr_AssignOp_Plus" => Op\Expr\AssignOp\Plus::class, "Expr_AssignOp_Pow" => Op\Expr\AssignOp\Pow::class, "Expr_AssignOp_ShiftLeft" => Op\Expr\AssignOp\ShiftLeft::class, "Expr_AssignOp_ShiftRight" => Op\Expr\AssignOp\ShiftRight::class][$expr->getType()]; if (empty($class)) { throw new \RuntimeException("AssignOp Not Found: " . $expr->getType()); } $this->block->children[] = $op = new $class($v, $e, $this->mapAttributes($expr)); return $op->result; } elseif ($expr instanceof Node\Expr\BinaryOp) { $left = $this->parseExprNode($expr->left); $right = $this->parseExprNode($expr->right); $class = ["Expr_BinaryOp_BitwiseAnd" => Op\Expr\BinaryOp\BitwiseAnd::class, "Expr_BinaryOp_BitwiseOr" => Op\Expr\BinaryOp\BitwiseOr::class, "Expr_BinaryOp_BitwiseXor" => Op\Expr\BinaryOp\BitwiseXor::class, "Expr_BinaryOp_BooleanAnd" => Op\Expr\BinaryOp\BooleanAnd::class, "Expr_BinaryOp_BooleanOr" => Op\Expr\BinaryOp\BooleanOr::class, "Expr_BinaryOp_Coalesce" => Op\Expr\BinaryOp\Coalesce::class, "Expr_BinaryOp_Concat" => Op\Expr\BinaryOp\Concat::class, "Expr_BinaryOp_Div" => Op\Expr\BinaryOp\Div::class, "Expr_BinaryOp_Equal" => Op\Expr\BinaryOp\Equal::class, "Expr_BinaryOp_Greater" => Op\Expr\BinaryOp\Greater::class, "Expr_BinaryOp_GreaterOrEqual" => Op\Expr\BinaryOp\GreaterOrEqual::class, "Expr_BinaryOp_Identical" => Op\Expr\BinaryOp\Identical::class, "Expr_BinaryOp_LogicalAnd" => Op\Expr\BinaryOp\LogicalAnd::class, "Expr_BinaryOp_LogicalOr" => Op\Expr\BinaryOp\LogicalOr::class, "Expr_BinaryOp_LogicalXor" => Op\Expr\BinaryOp\LogicalXor::class, "Expr_BinaryOp_Minus" => Op\Expr\BinaryOp\Minus::class, "Expr_BinaryOp_Mod" => Op\Expr\BinaryOp\Mod::class, "Expr_BinaryOp_Mul" => Op\Expr\BinaryOp\Mul::class, "Expr_BinaryOp_NotEqual" => Op\Expr\BinaryOp\NotEqual::class, "Expr_BinaryOp_NotIdentical" => Op\Expr\BinaryOp\NotIdentical::class, "Expr_BinaryOp_Plus" => Op\Expr\BinaryOp\Plus::class, "Expr_BinaryOp_Pow" => Op\Expr\BinaryOp\Pow::class, "Expr_BinaryOp_ShiftLeft" => Op\Expr\BinaryOp\ShiftLeft::class, "Expr_BinaryOp_ShiftRight" => Op\Expr\BinaryOp\ShiftRight::class, "Expr_BinaryOp_Smaller" => Op\Expr\BinaryOp\Smaller::class, "Expr_BinaryOp_SmallerOrEqual" => Op\Expr\BinaryOp\SmallerOrEqual::class, "Expr_BinaryOp_Spaceship" => Op\Expr\BinaryOp\Spaceship::class][$expr->getType()]; if (empty($class)) { throw new \RuntimeException("BinaryOp Not Found: " . $expr->getType()); } $this->block->children[] = $op = new $class($left, $right, $this->mapAttributes($expr)); return $op->result; } elseif ($expr instanceof Node\Expr\Cast) { $e = $this->parseExprNode($expr->expr); $class = ["Expr_Cast_Array" => Op\Expr\Cast\Array_::class, "Expr_Cast_Bool" => Op\Expr\Cast\Bool_::class, "Expr_Cast_Double" => Op\Expr\Cast\Double::class, "Expr_Cast_Int" => Op\Expr\Cast\Int_::class, "Expr_Cast_Object" => Op\Expr\Cast\Object_::class, "Expr_Cast_String" => Op\Expr\Cast\String_::class, "Expr_Cast_Unset" => Op\Expr\Cast\Unset_::class][$expr->getType()]; if (empty($class)) { throw new \RuntimeException("Cast Not Found: " . $expr->getType()); } $this->block->children[] = $op = new $class($e, $this->mapAttributes($expr)); return $op->result; } $op = null; $attrs = $this->mapAttributes($expr); switch ($expr->getType()) { case 'Arg': // TODO: Handle var-args return $this->parseExprNode($expr->value); case 'Expr_Array': $keys = []; $values = []; $byRef = []; if ($expr->items) { foreach ($expr->items as $item) { if ($item->key) { $keys[] = $this->parseExprNode($item->key); } else { $keys[] = null; } $values[] = $this->parseExprNode($item->value); $byRef[] = $item->byRef; } } $op = new Op\Expr\Array_($keys, $values, $byRef, $attrs); break; case 'Expr_ArrayDimFetch': $v = $this->parseExprNode($expr->var); $d = $this->parseExprNode($expr->dim); $op = new Op\Expr\ArrayDimFetch($v, $d, $attrs); break; case 'Expr_Assign': $e = $this->parseExprNode($expr->expr); $v = $this->parseExprNode($expr->var); $op = new Op\Expr\Assign($v, $e, $attrs); break; case 'Expr_AssignRef': $e = $this->parseExprNode($expr->expr); $v = $this->parseExprNode($expr->var); $op = new Op\Expr\AssignRef($v, $e, $attrs); break; case 'Expr_BitwiseNot': $op = new Op\Expr\BitwiseNot($this->parseExprNode($expr->expr), $attrs); break; case 'Expr_BooleanNot': $op = new Op\Expr\BooleanNot($this->parseExprNode($expr->expr), $attrs); break; case 'Expr_Closure': $block = new Block(); $this->parseNodes($expr->stmts, $block); $op = new Op\Expr\Closure($this->parseParameterList($expr->params), $expr->byRef, $expr->returnType, $block, $attrs); break; case 'Expr_ClassConstFetch': $c = $this->parseExprNode($expr->class); $n = $this->parseExprNode($expr->name); $op = new Op\Expr\ClassConstFetch($c, $n, $attrs); break; case 'Expr_Clone': $op = new Op\Expr\Clone_($this->parseExprNode($expr->expr), $attrs); break; case 'Expr_ConstFetch': $op = new Op\Expr\ConstFetch($this->parseExprNode($expr->name), $attrs); break; case 'Expr_Empty': $op = new Op\Expr\Empty_($this->parseNodes([$expr->expr], new Block()), $attrs); break; case 'Expr_ErrorSuppress': $block = new ErrorSuppressBlock(); $this->block->children[] = new Op\Stmt\Jump($block, $attrs); $this->block = $block; $result = $this->parseExprNode($expr->expr); $end = new Block(); $this->block->children[] = new Op\Stmt\Jump($end, $attrs); $this->block = $end; return $result; case 'Expr_Eval': $op = new Op\Expr\Eval_($this->parseExprNode($expr->expr), $attrs); break; case 'Expr_Exit': $op = new Op\Expr\Exit_($this->parseExprNode($expr->expr), $attrs); break; case 'Expr_FuncCall': $op = new Op\Expr\FuncCall($this->parseExprNode($expr->name), $this->parseExprList($expr->args), $attrs); break; case 'Expr_Include': $op = new Op\Expr\Include_($this->parseExprNode($expr->expr), $expr->type, $attrs); break; case 'Expr_Instanceof': $op = new Op\Expr\InstanceOf_($this->parseExprNode($expr->expr), $this->parseExprNode($expr->class), $attrs); break; case 'Expr_Isset': $op = new Op\Expr\Isset_($this->parseNodes($expr->vars, new Block()), $attrs); break; case 'Expr_List': $op = new Op\Expr\List_($this->parseExprList($expr->vars), $attrs); break; case 'Expr_MethodCall': $op = new Op\Expr\MethodCall($this->parseExprNode($expr->var), $this->parseExprNode($expr->name), $this->parseExprList($expr->args), $attrs); break; case 'Expr_New': $op = new Op\Expr\New_($this->parseExprNode($expr->class), $this->parseExprList($expr->args), $attrs); break; case 'Expr_PostDec': $op = new Op\Expr\PostDec($this->parseExprNode($expr->var), $attrs); break; case 'Expr_PostInc': $op = new Op\Expr\PostInc($this->parseExprNode($expr->var), $attrs); break; case 'Expr_PreDec': $op = new Op\Expr\PreDec($this->parseExprNode($expr->var), $attrs); break; case 'Expr_PreInc': $op = new Op\Expr\PreInc($this->parseExprNode($expr->var), $attrs); break; case 'Expr_Print': $op = new Op\Expr\Print_($this->parseExprNode($expr->expr), $attrs); break; case 'Expr_PropertyFetch': $op = new Op\Expr\PropertyFetch($this->parseExprNode($expr->var), $this->parseExprNode($expr->name), $attrs); break; case 'Expr_StaticCall': $op = new Op\Expr\StaticCall($this->parseExprNode($expr->class), $this->parseExprNode($expr->name), $this->parseExprList($expr->args), $attrs); break; case 'Expr_StaticPropertyFetch': $op = new Op\Expr\StaticPropertyFetch($this->parseExprNode($expr->class), $this->parseExprNode($expr->name), $attrs); break; case 'Expr_Ternary': $cond = $this->parseExprNode($expr->cond); $ifBlock = $this->block->create(); $elseBlock = $this->block->create(); $endBlock = $this->block->create(); $result = new Temporary(); $this->block->children[] = new Op\Stmt\JumpIf($cond, $ifBlock, $elseBlock, $attrs); $this->block = $ifBlock; if ($expr->if) { $this->block->children[] = new Op\Expr\Assign($result, $this->parseExprNode($expr->if), $attrs); } else { $this->block->children[] = new Op\Expr\Assign($result, $cond, $attrs); } $this->block->children[] = new Op\Stmt\Jump($endBlock, $attrs); $this->block = $elseBlock; $this->block->children[] = new Op\Expr\Assign($result, $this->parseExprNode($expr->else), $attrs); $elseBlock->children[] = new Op\Stmt\Jump($endBlock, $attrs); $this->block = $endBlock; return $result; case 'Expr_UnaryMinus': $op = new Op\Expr\UnaryMinus($this->parseExprNode($expr->expr), $attrs); break; default: throw new \RuntimeException("Unknown Expr Type " . $expr->getType()); } if ($op) { $this->block->children[] = $op; return $op->result; } throw new \RuntimeException("Invalid state, should never happen"); }