Example #1
0
 protected function emitError($msg, Op $op)
 {
     echo $msg;
     echo " ";
     echo $op->getFile() . ":" . $op->getLine();
     echo "\n";
 }
Example #2
0
 private function dumpOp(Op $op)
 {
     $result = $op->getType();
     foreach ($op->getVariableNames() as $varName) {
         $result .= "\n    {$varName}: ";
         $result .= $this->indent($this->dumpOperand($op->{$varName}));
     }
     foreach ($op->getSubBlocks() as $subBlock) {
         $result .= "\n    {$subBlock}: " . $this->indent($this->dumpBlockRef($op->{$subBlock}));
     }
     return $result;
 }
Example #3
0
 public function enterOp(Op $op, Block $block)
 {
     foreach ($op->getVariableNames() as $name) {
         $var = $op->{$name};
         if (!is_array($var)) {
             $var = [$var];
         }
         foreach ($var as $v) {
             if (is_null($v)) {
                 continue;
             }
             $this->variables->attach($v);
         }
     }
 }
 public function leaveOp(Op $op, Block $block)
 {
     if (!$op instanceof Op\Stmt\JumpIf) {
         return null;
     }
     if (!$op->cond instanceof Operand\Literal) {
         // Non-constant op
         return null;
     }
     // TODO: Figure out how to eliminate redundant Phi vars (eliminated phi vars)
     if ($op->cond->value) {
         return new Op\Stmt\Jump($op->if, $op->getAttributes());
     }
     return new Op\Stmt\Jump($op->else, $op->getAttributes());
 }
Example #5
0
 protected function renderOp(Op $op)
 {
     $result = $op->getType();
     if ($op instanceof Op\CallableOp) {
         $result .= '<' . $op->name->value . '>';
         foreach ($op->getParams() as $key => $param) {
             $result .= $this->indent("\nParam[{$key}]: " . $this->renderOperand($param->result));
         }
     }
     if ($op instanceof Op\Expr\Assertion) {
         $result .= "<" . $this->renderAssertion($op->assertion) . ">";
     }
     foreach ($op->getVariableNames() as $varName) {
         $vars = $op->{$varName};
         if (!is_array($vars)) {
             $vars = [$vars];
         }
         foreach ($vars as $var) {
             if (!$var) {
                 continue;
             }
             $result .= "\n    {$varName}: ";
             $result .= $this->indent($this->renderOperand($var));
         }
     }
     $childBlocks = [];
     foreach ($op->getSubBlocks() as $blockName) {
         $sub = $op->{$blockName};
         if (is_null($sub)) {
             continue;
         }
         if (!is_array($sub)) {
             $sub = [$sub];
         }
         foreach ($sub as $subBlock) {
             if (!$subBlock) {
                 continue;
             }
             $this->enqueueBlock($subBlock);
             $childBlocks[] = ["block" => $subBlock, "name" => $blockName];
         }
     }
     return ["op" => $op, "label" => $result, "childBlocks" => $childBlocks];
 }
Example #6
0
 protected function renderOp(Op $op)
 {
     $result = $op->getType();
     if ($op instanceof Op\CallableOp) {
         $func = $op->getFunc();
         $result .= "<" . $func->name . ">";
     }
     if ($op instanceof Op\Expr\Assertion) {
         $result .= "<" . $this->renderAssertion($op->assertion) . ">";
     }
     foreach ($op->getVariableNames() as $varName) {
         $vars = $op->{$varName};
         if (is_array($vars)) {
             foreach ($vars as $key => $var) {
                 if (!$var) {
                     continue;
                 }
                 $result .= "\n    {$varName}[{$key}]: ";
                 $result .= $this->indent($this->renderOperand($var));
             }
         } elseif ($vars) {
             $result .= "\n    {$varName}: ";
             $result .= $this->indent($this->renderOperand($vars));
         }
     }
     $childBlocks = [];
     foreach ($op->getSubBlocks() as $blockName) {
         $sub = $op->{$blockName};
         if (is_array($sub)) {
             foreach ($sub as $key => $subBlock) {
                 if (!$subBlock) {
                     continue;
                 }
                 $this->enqueueBlock($subBlock);
                 $childBlocks[] = ["block" => $subBlock, "name" => $blockName . "[" . $key . "]"];
             }
         } elseif ($sub) {
             $this->enqueueBlock($sub);
             $childBlocks[] = ["block" => $sub, "name" => $blockName];
         }
     }
     return ["op" => $op, "label" => $result, "childBlocks" => $childBlocks];
 }
Example #7
0
 public static function removeUsage(Operand $var, Op $op)
 {
     foreach ($op->getVariableNames() as $varName) {
         $vars = $op->{$varName};
         $newVars = [];
         if (!is_array($vars)) {
             $vars = [$vars];
         }
         foreach ($vars as $key => $value) {
             if ($value !== $var) {
                 $newVars[$key] = $value;
             }
         }
         if (!is_array($op->{$varName})) {
             $op->{$varName} = array_shift($newVars);
         } else {
             $op->{$varName} = array_keys($newVars);
         }
     }
 }
Example #8
0
 protected function resolveVarOp(Operand $var, Op $op, \SplObjectStorage $resolved)
 {
     switch ($op->getType()) {
         case 'Expr_Array':
             $types = [];
             foreach ($op->values as $value) {
                 if (!isset($resolved[$value])) {
                     return false;
                 }
                 $types[] = $resolved[$value];
             }
             if (empty($types)) {
                 return [new Type(Type::TYPE_ARRAY)];
             }
             $r = $this->computeMergedType($types);
             if ($r) {
                 return [new Type(Type::TYPE_ARRAY, [$r])];
             }
         case 'Expr_Cast_Array':
             // Todo: determine subtypes better
             return [new Type(Type::TYPE_ARRAY)];
         case 'Expr_ArrayDimFetch':
             if ($resolved->contains($op->var)) {
                 // Todo: determine subtypes better
                 $type = $resolved[$op->var];
                 if ($type->subTypes) {
                     return $type->subTypes;
                 }
                 if ($type->type === Type::TYPE_STRING) {
                     return [$type];
                 }
                 return [Type::mixed()];
             }
             break;
         case 'Expr_Assign':
         case 'Expr_AssignRef':
             if ($resolved->contains($op->expr)) {
                 return [$resolved[$op->expr]];
             }
             break;
         case 'Expr_InstanceOf':
         case 'Expr_BinaryOp_Equal':
         case 'Expr_BinaryOp_NotEqual':
         case 'Expr_BinaryOp_Greater':
         case 'Expr_BinaryOp_GreaterOrEqual':
         case 'Expr_BinaryOp_Identical':
         case 'Expr_BinaryOp_NotIdentical':
         case 'Expr_BinaryOp_Smaller':
         case 'Expr_BinaryOp_SmallerOrEqual':
         case 'Expr_BinaryOp_LogicalAnd':
         case 'Expr_BinaryOp_LogicalOr':
         case 'Expr_BinaryOp_LogicalXor':
         case 'Expr_BooleanNot':
         case 'Expr_Cast_Bool':
         case 'Expr_Empty':
         case 'Expr_Isset':
             return [Type::bool()];
         case 'Expr_BinaryOp_BitwiseAnd':
         case 'Expr_BinaryOp_BitwiseOr':
         case 'Expr_BinaryOp_BitwiseXor':
             if ($resolved->contains($op->left) && $resolved->contains($op->right)) {
                 switch ([$resolved[$op->left]->type, $resolved[$op->right]->type]) {
                     case [Type::TYPE_STRING, Type::TYPE_STRING]:
                         return [Type::string()];
                     default:
                         return [Type::int()];
                 }
             }
             break;
         case 'Expr_BitwiseNot':
             if ($resolved->contains($op->expr)) {
                 if ($resolved[$op->expr]->type === Type::TYPE_STRING) {
                     return [Type::string()];
                 }
                 return [Type::int()];
             }
             break;
         case 'Expr_BinaryOp_Div':
         case 'Expr_BinaryOp_Plus':
         case 'Expr_BinaryOp_Minus':
         case 'Expr_BinaryOp_Mul':
             if ($resolved->contains($op->left) && $resolved->contains($op->right)) {
                 switch ([$resolved[$op->left]->type, $resolved[$op->right]->type]) {
                     case [Type::TYPE_LONG, Type::TYPE_LONG]:
                         return [Type::int()];
                     case [Type::TYPE_DOUBLE, TYPE::TYPE_LONG]:
                     case [Type::TYPE_LONG, TYPE::TYPE_DOUBLE]:
                     case [Type::TYPE_DOUBLE, TYPE::TYPE_DOUBLE]:
                         return [Type::float()];
                     case [Type::TYPE_ARRAY, Type::TYPE_ARRAY]:
                         $sub = $this->computeMergedType(array_merge($resolved[$op->left]->subTypes, $resolved[$op->right]->subTypes));
                         if ($sub) {
                             return [new Type(Type::TYPE_ARRAY, [$sub])];
                         }
                         return [new Type(Type::TYPE_ARRAY)];
                     default:
                         throw new \RuntimeException("Math op on unknown types {$resolved[$op->left]} + {$resolved[$op->right]}");
                 }
             }
             break;
         case 'Expr_BinaryOp_Concat':
         case 'Expr_Cast_String':
         case 'Expr_ConcatList':
             return [Type::string()];
         case 'Expr_BinaryOp_Mod':
         case 'Expr_BinaryOp_ShiftLeft':
         case 'Expr_BinaryOp_ShiftRight':
         case 'Expr_Cast_Int':
         case 'Expr_Print':
             return [Type::int()];
         case 'Expr_Cast_Double':
             return [Type::float()];
         case 'Expr_Cast_Object':
             if ($resolved->contains($op->expr)) {
                 if ($resolved[$op->expr]->type->resolves(Type::object())) {
                     return [$resolved[$op->expr]];
                 }
                 return [new Type(Type::TYPE_OBJECT, [], 'stdClass')];
             }
             break;
         case 'Expr_Clone':
             if ($resolved->contains($op->expr)) {
                 return [$resolved[$op->expr]];
             }
             break;
         case 'Expr_Closure':
             return [new Type(Type::TYPE_OBJECT, [], "Closure")];
         case 'Expr_FuncCall':
             if ($op->name instanceof Operand\Literal) {
                 $name = strtolower($op->name->value);
                 if (isset($this->components['functionLookup'][$name])) {
                     $result = [];
                     foreach ($this->components['functionLookup'][$name] as $func) {
                         if ($func->returnType) {
                             $result[] = Type::fromDecl($func->returnType->value);
                         } else {
                             // Check doc comment
                             $result[] = Type::extractTypeFromComment("return", $func->getAttribute('doccomment'));
                         }
                     }
                     return $result;
                 } else {
                     if (isset($this->components['internalTypeInfo']->functions[$name])) {
                         $type = $this->components['internalTypeInfo']->functions[$name];
                         if (empty($type['return'])) {
                             return false;
                         }
                         return [Type::fromDecl($type['return'])];
                     }
                 }
             }
             // we can't resolve the function
             return false;
         case 'Expr_List':
             if ($op->result === $var) {
                 return [new Type(Type::TYPE_ARRAY)];
             }
             // TODO: infer this
             return false;
         case 'Expr_New':
             $type = $this->getClassType($op->class, $resolved);
             if ($type) {
                 return [$type];
             }
             return [Type::object()];
         case 'Expr_Param':
             $docType = Type::extractTypeFromComment("param", $op->function->getAttribute('doccomment'), $op->name->value);
             if ($op->type) {
                 $type = Type::fromDecl($op->type->value);
                 if ($op->defaultVar) {
                     if ($op->defaultBlock->children[0]->getType() === "Expr_ConstFetch" && strtolower($op->defaultBlock->children[0]->name->value) === "null") {
                         $type = (new Type(Type::TYPE_UNION, [$type, Type::null()]))->simplify();
                     }
                 }
                 if ($docType !== Type::mixed() && $this->components['typeResolver']->resolves($docType, $type)) {
                     // return the more specific
                     return [$docType];
                 }
                 return [$type];
             }
             return [$docType];
         case 'Expr_PropertyFetch':
         case 'Expr_StaticPropertyFetch':
             if (!$op->name instanceof Operand\Literal) {
                 // variable property fetch
                 return [Type::mixed()];
             }
             $propName = $op->name->value;
             if ($op instanceof Op\Expr\StaticPropertyFetch) {
                 $objType = $this->getClassType($op->class, $resolved);
             } else {
                 $objType = $this->getClassType($op->var, $resolved);
             }
             if ($objType) {
                 return $this->resolveProperty($objType, $propName);
             }
             return false;
         case 'Expr_Yield':
         case 'Expr_Include':
             // TODO: we may be able to determine these...
             return false;
         case 'Expr_Assertion':
             $tmp = $this->processAssertion($op->assertion, $op->expr, $resolved);
             if ($tmp) {
                 return [$tmp];
             }
             return false;
         case 'Expr_TypeUnAssert':
             throw new \RuntimeException("Unassertions should not occur anymore");
         case 'Expr_UnaryMinus':
         case 'Expr_UnaryPlus':
             if ($resolved->contains($op->expr)) {
                 switch ($resolved[$op->expr]->type) {
                     case Type::TYPE_LONG:
                     case Type::TYPE_DOUBLE:
                         return [$resolved[$op->expr]];
                 }
                 return [Type::numeric()];
             }
             break;
         case 'Expr_Eval':
             return false;
         case 'Iterator_Key':
             if ($resolved->contains($op->var)) {
                 // TODO: implement this as well
                 return false;
             }
             break;
         case 'Expr_Exit':
         case 'Iterator_Reset':
             return [Type::null()];
         case 'Iterator_Valid':
             return [Type::bool()];
         case 'Iterator_Value':
             if ($resolved->contains($op->var)) {
                 if ($resolved[$op->var]->subTypes) {
                     return $resolved[$op->var]->subTypes;
                 }
                 return false;
             }
             break;
         case 'Expr_StaticCall':
             return $this->resolveMethodCall($op->class, $op->name, $op, $resolved);
         case 'Expr_MethodCall':
             return $this->resolveMethodCall($op->var, $op->name, $op, $resolved);
         case 'Expr_ConstFetch':
             if ($op->name instanceof Operand\Literal) {
                 $constant = strtolower($op->name->value);
                 switch ($constant) {
                     case 'true':
                     case 'false':
                         return [Type::bool()];
                     case 'null':
                         return [Type::null()];
                     default:
                         if (isset($this->components['constants'][$op->name->value])) {
                             $return = [];
                             foreach ($this->components['constants'][$op->name->value] as $value) {
                                 if (!$resolved->contains($value->value)) {
                                     return false;
                                 }
                                 $return[] = $resolved[$value->value];
                             }
                             return $return;
                         }
                 }
             }
             return false;
         case 'Expr_ClassConstFetch':
             //TODO
             $classes = [];
             if ($op->class instanceof Operand\Literal) {
                 $class = strtolower($op->class->value);
                 return $this->resolveClassConstant($class, $op, $resolved);
             } elseif ($resolved->contains($op->class)) {
                 $type = $resolved[$op->class];
                 if ($type->type !== Type::TYPE_OBJECT || empty($type->userType)) {
                     // give up
                     return false;
                 }
                 return $this->resolveClassConstant(strtolower($type->userType), $op, $resolved);
             }
             return false;
         case 'Phi':
             $types = [];
             $resolveFully = true;
             foreach ($op->vars as $v) {
                 if ($resolved->contains($v)) {
                     $types[] = $resolved[$v];
                 } else {
                     $resolveFully = false;
                 }
             }
             if (empty($types)) {
                 return false;
             }
             $type = $this->computeMergedType($types);
             if ($type) {
                 if ($resolveFully) {
                     return [$type];
                 }
                 // leave on unresolved list to try again next round
                 $resolved[$var] = $type;
             }
             return false;
         default:
             throw new \RuntimeException("Unknown operand prefix type: " . $op->getType());
     }
     return false;
 }
Example #9
0
 public function __construct(array $attributes = [])
 {
     parent::__construct($attributes);
 }
 public function leaveOp(Op $op, Block $block)
 {
     if (!$op instanceof Op\Expr\BinaryOp) {
         return null;
     }
     if (!$op->left instanceof Operand\Literal || !$op->right instanceof Operand\Literal) {
         // Non-constant op
         return null;
     }
     switch ($op->getType()) {
         case 'Expr_BinaryOp_BitwiseAnd':
             $newValue = new Operand\Literal($op->left->value & $op->right->value);
             break;
         case 'Expr_BinaryOp_BitwiseOr':
             $newValue = new Operand\Literal($op->left->value | $op->right->value);
             break;
         case 'Expr_BinaryOp_BitwiseXor':
             $newValue = new Operand\Literal($op->left->value ^ $op->right->value);
             break;
         case 'Expr_BinaryOp_Coalesce':
             if ($op->left->value === null) {
                 throw new \RuntimeException("Not possible yet");
             }
             $newValue = new Operand\Literal($op->left->value);
             break;
         case 'Expr_BinaryOp_Concat':
             $newValue = new Operand\Literal($op->left->value . $op->right->value);
             break;
         case 'Expr_BinaryOp_Div':
             $newValue = new Operand\Literal($op->left->value / $op->right->value);
             break;
         case 'Expr_BinaryOp_Equal':
             $newValue = new Operand\Literal($op->left->value == $op->right->value);
             break;
         case 'Expr_BinaryOp_Greater':
             $newValue = new Operand\Literal($op->left->value > $op->right->value);
             break;
         case 'Expr_BinaryOp_GreaterOrEqual':
             $newValue = new Operand\Literal($op->left->value >= $op->right->value);
             break;
         case 'Expr_BinaryOp_Identical':
             $newValue = new Operand\Literal($op->left->value === $op->right->value);
             break;
         case 'Expr_BinaryOp_LogicalXor':
             $newValue = new Operand\Literal($op->left->value xor $op->right->value);
             break;
         case 'Expr_BinaryOp_Minus':
             $newValue = new Operand\Literal($op->left->value - $op->right->value);
             break;
         case 'Expr_BinaryOp_Mod':
             $newValue = new Operand\Literal($op->left->value % $op->right->value);
             break;
         case 'Expr_BinaryOp_Mul':
             $newValue = new Operand\Literal($op->left->value * $op->right->value);
             break;
         case 'Expr_BinaryOp_NotEqual':
             $newValue = new Operand\Literal($op->left->value != $op->right->value);
             break;
         case 'Expr_BinaryOp_NotIdentical':
             $newValue = new Operand\Literal($op->left->value !== $op->right->value);
             break;
         case 'Expr_BinaryOp_Plus':
             $newValue = new Operand\Literal($op->left->value + $op->right->value);
             break;
         case 'Expr_BinaryOp_Pow':
             $newValue = new Operand\Literal(pow($op->left->value, $op->right->value));
             break;
         case 'Expr_BinaryOp_ShiftLeft':
             $newValue = new Operand\Literal($op->left->value << $op->right->value);
             break;
         case 'Expr_BinaryOp_ShiftRight':
             $newValue = new Operand\Literal($op->left->value >> $op->right->value);
             break;
         case 'Expr_BinaryOp_Smaller':
             $newValue = new Operand\Literal($op->left->value < $op->right->value);
             break;
         case 'Expr_BinaryOp_SmallerOrEqual':
             $newValue = new Operand\Literal($op->left->value <= $op->right->value);
             break;
         case 'Expr_BinaryOp_Spaceship':
             $value = 0;
             if ($op->left->value < $op->right->value) {
                 $value = -1;
             } elseif ($op->left->value > $op->right->value) {
                 $value = 1;
             }
             $newValue = new Operand\Literal($value);
             break;
         default:
             throw new \RuntimeException("Unknown constant op found: " . $op->getType());
     }
     $newValue->type = Type::fromValue($newValue->value);
     Helper::replaceVar($op->result, $newValue);
     return Visitor::REMOVE_OP;
 }
Example #11
0
 public function __construct(array $attributes = [])
 {
     parent::__construct($attributes);
     $this->result = $this->addWriteRef(new Temporary());
 }
Example #12
0
 public function leaveOp(Op $op, Block $block)
 {
     echo "Leave Op " . $op->getType() . "\n";
 }
Example #13
0
 public function __construct(array $attributes = array())
 {
     parent::__construct($attributes);
     $this->result = new Variable();
 }
Example #14
0
 protected function resolveVarOp(Operand $var, Op $op, SplObjectStorage $resolved)
 {
     $method = 'resolveOp_' . $op->getType();
     if (method_exists($this, $method)) {
         return call_user_func([$this, $method], $var, $op, $resolved);
     }
     switch ($op->getType()) {
         case 'Expr_InstanceOf':
         case 'Expr_BinaryOp_Equal':
         case 'Expr_BinaryOp_NotEqual':
         case 'Expr_BinaryOp_Greater':
         case 'Expr_BinaryOp_GreaterOrEqual':
         case 'Expr_BinaryOp_Identical':
         case 'Expr_BinaryOp_NotIdentical':
         case 'Expr_BinaryOp_Smaller':
         case 'Expr_BinaryOp_SmallerOrEqual':
         case 'Expr_BinaryOp_LogicalAnd':
         case 'Expr_BinaryOp_LogicalOr':
         case 'Expr_BinaryOp_LogicalXor':
         case 'Expr_BooleanNot':
         case 'Expr_Cast_Bool':
         case 'Expr_Empty':
         case 'Expr_Isset':
             return [Type::bool()];
         case 'Expr_BinaryOp_BitwiseAnd':
         case 'Expr_BinaryOp_BitwiseOr':
         case 'Expr_BinaryOp_BitwiseXor':
             if ($resolved->contains($op->left) && $resolved->contains($op->right)) {
                 switch ([$resolved[$op->left]->type, $resolved[$op->right]->type]) {
                     case [Type::TYPE_STRING, Type::TYPE_STRING]:
                         return [Type::string()];
                     default:
                         return [Type::int()];
                 }
             }
             return false;
         case 'Expr_BitwiseNot':
             if ($resolved->contains($op->expr)) {
                 switch ($resolved[$op->expr]->type) {
                     case Type::TYPE_STRING:
                         return [Type::string()];
                     default:
                         return [Type::int()];
                 }
             }
             return false;
         case 'Expr_BinaryOp_Div':
         case 'Expr_BinaryOp_Plus':
         case 'Expr_BinaryOp_Minus':
         case 'Expr_BinaryOp_Mul':
             if ($resolved->contains($op->left) && $resolved->contains($op->right)) {
                 switch ([$resolved[$op->left]->type, $resolved[$op->right]->type]) {
                     case [Type::TYPE_LONG, Type::TYPE_LONG]:
                         return [Type::int()];
                     case [Type::TYPE_DOUBLE, TYPE::TYPE_LONG]:
                     case [Type::TYPE_LONG, TYPE::TYPE_DOUBLE]:
                     case [Type::TYPE_DOUBLE, TYPE::TYPE_DOUBLE]:
                         return [Type::float()];
                     case [Type::TYPE_ARRAY, Type::TYPE_ARRAY]:
                         $sub = $this->computeMergedType(array_merge($resolved[$op->left]->subTypes, $resolved[$op->right]->subTypes));
                         if ($sub) {
                             return [new Type(Type::TYPE_ARRAY, [$sub])];
                         }
                         return [new Type(Type::TYPE_ARRAY)];
                     default:
                         return [Type::mixed()];
                         throw new \RuntimeException("Math op on unknown types {$resolved[$op->left]} + {$resolved[$op->right]}");
                 }
             }
             return false;
         case 'Expr_BinaryOp_Concat':
         case 'Expr_Cast_String':
         case 'Expr_ConcatList':
             return [Type::string()];
         case 'Expr_BinaryOp_Mod':
         case 'Expr_BinaryOp_ShiftLeft':
         case 'Expr_BinaryOp_ShiftRight':
         case 'Expr_Cast_Int':
         case 'Expr_Print':
             return [Type::int()];
         case 'Expr_Cast_Double':
             return [Type::float()];
         case 'Expr_UnaryMinus':
         case 'Expr_UnaryPlus':
             if ($resolved->contains($op->expr)) {
                 switch ($resolved[$op->expr]->type) {
                     case Type::TYPE_LONG:
                     case Type::TYPE_DOUBLE:
                         return [$resolved[$op->expr]];
                 }
                 return [Type::numeric()];
             }
             return false;
         case 'Expr_Eval':
             return false;
         case 'Iterator_Key':
             if ($resolved->contains($op->var)) {
                 // TODO: implement this as well
                 return false;
             }
             return false;
         case 'Expr_Exit':
         case 'Iterator_Reset':
             return [Type::null()];
         case 'Iterator_Valid':
             return [Type::bool()];
         case 'Iterator_Value':
             if ($resolved->contains($op->var)) {
                 if ($resolved[$op->var]->subTypes) {
                     return $resolved[$op->var]->subTypes;
                 }
                 return false;
             }
             return false;
         case 'Expr_StaticCall':
             return $this->resolveMethodCall($op->class, $op->name, $op, $resolved);
         case 'Expr_MethodCall':
             return $this->resolveMethodCall($op->var, $op->name, $op, $resolved);
         case 'Expr_Yield':
         case 'Expr_Include':
             // TODO: we may be able to determine these...
             return false;
     }
     throw new \LogicException("Unknown variable op found: " . $op->getType());
 }
Example #15
0
 protected function resolveVarOp(Operand $var, Op $op, \SplObjectStorage $resolved)
 {
     switch ($op->getType()) {
         case 'Expr_Array':
             $types = [];
             foreach ($op->values as $value) {
                 if (!isset($resolved[$value])) {
                     return false;
                 }
                 $types[] = $resolved[$value];
             }
             $r = $this->computeMergedType($types);
             if ($r) {
                 return [new Type(Type::TYPE_ARRAY, [$r])];
             }
         case 'Expr_Cast_Array':
             // Todo: determine subtypes better
             return [new Type(Type::TYPE_ARRAY)];
         case 'Expr_ArrayDimFetch':
             if ($resolved->contains($op->var)) {
                 // Todo: determine subtypes better
                 $type = $resolved[$op->var];
                 if ($type->subTypes) {
                     return $type->subTypes;
                 }
                 if ($type->type === Type::TYPE_STRING) {
                     return [$type];
                 }
                 return [new Type(Type::TYPE_MIXED)];
             }
             break;
         case 'Expr_Assign':
         case 'Expr_AssignRef':
             if ($resolved->contains($op->expr)) {
                 return [$resolved[$op->expr]];
             }
             break;
         case 'Expr_BinaryOp_Equal':
         case 'Expr_BinaryOp_NotEqual':
         case 'Expr_BinaryOp_Greater':
         case 'Expr_BinaryOp_GreaterOrEqual':
         case 'Expr_BinaryOp_Identical':
         case 'Expr_BinaryOp_NotIdentical':
         case 'Expr_BinaryOp_Smaller':
         case 'Expr_BinaryOp_SmallerOrEqual':
         case 'Expr_BinaryOp_LogicalAnd':
         case 'Expr_BinaryOp_LogicalOr':
         case 'Expr_BinaryOp_LogicalXor':
         case 'Expr_BooleanNot':
         case 'Expr_Cast_Bool':
         case 'Expr_Empty':
         case 'Expr_InstanceOf':
         case 'Expr_Isset':
             return [new Type(Type::TYPE_BOOLEAN)];
         case 'Expr_BinaryOp_BitwiseAnd':
         case 'Expr_BinaryOp_BitwiseOr':
         case 'Expr_BinaryOp_BitwiseXor':
             if ($resolved->contains($op->left) && $resolved->contains($op->right)) {
                 switch ([$resolved[$op->left]->type, $resolved[$op->right]->type]) {
                     case [Type::TYPE_STRING, Type::TYPE_STRING]:
                         return [new Type(Type::TYPE_STRING)];
                     default:
                         return [new Type(Type::TYPE_LONG)];
                 }
             }
             break;
         case 'Expr_BitwiseNot':
             if ($resolved->contains($op->expr)) {
                 if ($resolved[$op->expr]->type === Type::TYPE_STRING) {
                     return [new Type(Type::TYPE_STRING)];
                 }
                 return [new Type(Type::TYPE_LONG)];
             }
             break;
         case 'Expr_BinaryOp_Div':
         case 'Expr_BinaryOp_Plus':
         case 'Expr_BinaryOp_Minus':
         case 'Expr_BinaryOp_Mul':
             if ($resolved->contains($op->left) && $resolved->contains($op->right)) {
                 switch ([$resolved[$op->left]->type, $resolved[$op->right]->type]) {
                     case [Type::TYPE_LONG, Type::TYPE_LONG]:
                         return [new Type(Type::TYPE_LONG)];
                     case [Type::TYPE_DOUBLE, TYPE::TYPE_LONG]:
                     case [Type::TYPE_LONG, TYPE::TYPE_DOUBLE]:
                     case [Type::TYPE_DOUBLE, TYPE::TYPE_DOUBLE]:
                     case [Type::TYPE_MIXED, TYPE::TYPE_DOUBLE]:
                     case [Type::TYPE_DOUBLE, TYPE::TYPE_MIXED]:
                     case [Type::TYPE_NUMERIC, TYPE::TYPE_DOUBLE]:
                     case [Type::TYPE_DOUBLE, TYPE::TYPE_NUMERIC]:
                         return [new Type(Type::TYPE_DOUBLE)];
                     case [Type::TYPE_MIXED, Type::TYPE_MIXED]:
                     case [Type::TYPE_MIXED, Type::TYPE_LONG]:
                     case [Type::TYPE_LONG, Type::TYPE_MIXED]:
                     case [Type::TYPE_NUMERIC, Type::TYPE_LONG]:
                     case [Type::TYPE_LONG, Type::TYPE_NUMERIC]:
                     case [Type::TYPE_NUMERIC, Type::TYPE_MIXED]:
                     case [Type::TYPE_MIXED, Type::TYPE_NUMERIC]:
                     case [Type::TYPE_NUMERIC, Type::TYPE_NUMERIC]:
                         return [new Type(Type::TYPE_NUMERIC)];
                     case [Type::TYPE_ARRAY, Type::TYPE_ARRAY]:
                         $sub = $this->computeMergedType(array_merge($resolved[$op->left]->subTypes), $resolved[$op->right]->subTypes);
                         if ($sub) {
                             return [new Type(Type::TYPE_ARRAY, [$sub])];
                         }
                         return [new Type(Type::TYPE_ARRAY)];
                     default:
                         return [new Type(Type::TYPE_MIXED)];
                 }
             }
             break;
         case 'Expr_BinaryOp_Concat':
         case 'Expr_Cast_String':
         case 'Expr_ConcatList':
             return [new Type(Type::TYPE_STRING)];
         case 'Expr_BinaryOp_Mod':
         case 'Expr_BinaryOp_ShiftLeft':
         case 'Expr_BinaryOp_ShiftRight':
         case 'Expr_Cast_Int':
         case 'Expr_Print':
             return [new Type(Type::TYPE_LONG)];
         case 'Expr_Cast_Double':
             return [new Type(Type::TYPE_DOUBLE)];
         case 'Expr_Cast_Object':
             if ($resolved->contains($op->expr)) {
                 if ($resolved[$op->expr]->type === Type::TYPE_USER) {
                     return [$resolved[$op->expr]];
                 }
                 return [new Type(Type::TYPE_USER, null, 'stdClass')];
             }
             break;
         case 'Expr_Clone':
             if ($resolved->contains($op->expr)) {
                 return [$resolved[$op->expr]];
             }
             break;
         case 'Expr_Closure':
             return [new Type(Type::TYPE_USER, [], ["Closure"])];
         case 'Expr_FuncCall':
             if ($op->name instanceof Operand\Literal) {
                 $name = strtolower($op->name->value);
                 if (isset($this->components['functionLookup'][$name])) {
                     $result = [];
                     foreach ($this->components['functionLookup'][$name] as $func) {
                         if ($func->returnType) {
                             $result[] = Type::fromDecl($func->returnType->value);
                         } else {
                             // Check doc comment
                             $result[] = Type::extractTypeFromComment("return", $func->getAttribute('doccomment'));
                         }
                     }
                     return $result;
                 } else {
                     if (isset($this->components['internalTypeInfo']->functions[$name])) {
                         $type = $this->components['internalTypeInfo']->functions[$name];
                         if (empty($type['return'])) {
                             return [new Type(Type::TYPE_MIXED)];
                         }
                         return [Type::fromDecl($type['return'])];
                     }
                 }
             }
             // we can't resolve the function
             return [new Type(Type::TYPE_MIXED)];
             break;
         case 'Expr_List':
             if ($op->result === $var) {
                 return [new Type(Type::TYPE_ARRAY)];
             }
             // TODO: infer this
             return [new Type(Type::TYPE_MIXED)];
         case 'Expr_New':
             if ($op->class instanceof Operand\Literal) {
                 return [new Type(Type::TYPE_USER, [], [$op->class->value])];
             }
             return [new Type(Type::TYPE_OBJECT)];
         case 'Expr_Param':
             if ($op->type) {
                 $type = Type::fromDecl($op->type->value);
                 if ($op->defaultVar) {
                     if ($op->defaultBlock->children[0]->getType() === "Expr_ConstFetch" && strtolower($op->defaultBlock->children[0]->name->value) === "null") {
                         $type->type |= Type::TYPE_NULL;
                     }
                 }
                 return [$type];
             }
             return [Type::extractTypeFromComment("param", $op->function->getAttribute('doccomment'), $op->name->value)];
         case 'Expr_Yield':
         case 'Expr_Include':
         case 'Expr_PropertyFetch':
         case 'Expr_StaticPropertyFetch':
         case 'Stmt_Property':
             // TODO: we may be able to determine these...
             return [new Type(Type::TYPE_MIXED)];
         case 'Expr_TypeAssert':
             return [Type::fromDecl($op->assertedType)];
         case 'Expr_TypeUnAssert':
             if ($resolved->contains($op->assert->expr)) {
                 return [$resolved[$op->assert->expr]];
             }
         case 'Expr_UnaryMinus':
         case 'Expr_UnaryPlus':
             if ($resolved->contains($op->expr)) {
                 switch ($resolved[$op->expr]->type) {
                     case Type::TYPE_LONG:
                     case Type::TYPE_DOUBLE:
                         return [$resolved[$op->expr]];
                 }
                 return [new Type(Type::TYPE_NUMERIC)];
             }
             break;
         case 'Expr_Eval':
             return [new Type(Type::TYPE_MIXED)];
         case 'Iterator_Key':
             if ($resolved->contains($op->var)) {
                 // TODO: implement this as well
                 return [new Type(Type::TYPE_MIXED)];
             }
             break;
         case 'Expr_Exit':
         case 'Iterator_Reset':
             return [new Type(Type::TYPE_VOID)];
         case 'Iterator_Valid':
             return [new Type(Type::TYPE_BOOLEAN)];
         case 'Iterator_Value':
             if ($resolved->contains($op->var)) {
                 if ($resolved[$op->var]->subTypes) {
                     return $resolved[$op->var]->subTypes;
                 }
                 return [new Type(Type::TYPE_MIXED)];
             }
             break;
         case 'Expr_MethodCall':
             if (!$op->name instanceof Operand\Literal) {
                 // variable method call
                 return [new Type(Type::TYPE_MIXED)];
             }
             if ($resolved->contains($op->var)) {
                 if ($resolved[$op->var]->type !== Type::TYPE_USER) {
                     return [new Type(Type::TYPE_MIXED)];
                 }
                 $types = [];
                 foreach ($resolved[$op->var]->userTypes as $ut) {
                     $className = strtolower($ut);
                     if (!isset($this->components['resolves'][$className])) {
                         return [new Type(Type::TYPE_MIXED)];
                     }
                     foreach ($this->components['resolves'][$className] as $class) {
                         $method = $this->findMethod($class, $op->name->value);
                         if (!$method) {
                             continue;
                         }
                         if (!$method->returnType) {
                             $types[] = Type::extractTypeFromComment("return", $method->getAttribute('doccomment'));
                         } else {
                             $types[] = Type::fromDecl($method->returnType->value);
                         }
                     }
                 }
                 return $types;
             }
             break;
         case 'Expr_ConstFetch':
             if ($op->name instanceof Operand\Literal) {
                 $constant = strtolower($op->name->value);
                 switch ($constant) {
                     case 'true':
                     case 'false':
                         return [new Type(Type::TYPE_BOOLEAN)];
                     case 'null':
                         return [new Type(Type::TYPE_NULL)];
                     default:
                         if (isset($this->components['constants'][$op->name->value])) {
                             $return = [];
                             foreach ($this->components['constants'][$op->name->value] as $value) {
                                 if (!$resolved->contains($value->value)) {
                                     return false;
                                 }
                                 $return[] = $resolved[$value->value];
                             }
                             return $return;
                         }
                 }
             }
             return [new Type(Type::TYPE_MIXED)];
         case 'Expr_StaticCall':
             return [new Type(Type::TYPE_MIXED)];
         case 'Expr_ClassConstFetch':
             //TODO
             $classes = [];
             if ($op->class instanceof Operand\Literal) {
                 $class = strtolower($op->class->value);
                 return $this->resolveClassConstant($class, $op, $resolved);
             } elseif ($resolved->contains($op->class)) {
                 $type = $resolved[$op->class];
                 if ($type->type !== Type::TYPE_USER) {
                     // give up
                     return [new Type(Type::TYPE_MIXED)];
                 }
                 $types = [];
                 foreach ($type->userTypes as $type) {
                     $try = $this->resolveClassConstant(strtolower($type), $op, $resolved);
                     if ($try) {
                         $types = array_merge($types, $try);
                     } else {
                         return false;
                     }
                 }
                 if ($types) {
                     return $types;
                 }
                 return [new Type(Type::TYPE_MIXED)];
             }
             return false;
         case 'Phi':
             $types = [];
             foreach ($op->vars as $var) {
                 if ($resolved->contains($var)) {
                     $types[] = $resolved[$var];
                 } else {
                     // entire phi isn't resolved yet, can't process
                     continue 2;
                 }
             }
             if (empty($types)) {
                 return false;
             }
             $type = $this->computeMergedType($types);
             if ($type) {
                 return [$type];
             }
             return false;
         default:
             throw new \RuntimeException("Unknown operand prefix type: " . $op->getType());
     }
     return false;
 }
Example #16
0
 public function __construct(Operand $result, array $attributes = [])
 {
     parent::__construct($attributes);
     $this->result = $this->addWriteRef($result);
 }
Example #17
0
 private function replaceOpVariable(Operand $from, Operand $to, Op $op)
 {
     foreach ($op->getVariableNames() as $name) {
         if (is_null($op->{$name})) {
             continue;
         }
         if (is_array($op->{$name})) {
             // SIGH, PHP won't let me do this directly (parses as $op->($name[$key]))
             $result = $op->{$name};
             $new = [];
             foreach ($result as $key => $value) {
                 if ($value === $from) {
                     $new[$key] = $to;
                     if ($op->isWriteVariable($name)) {
                         $to->addWriteOp($op);
                     } else {
                         $to->addUsage($op);
                     }
                 } else {
                     $new[$key] = $value;
                 }
             }
             $op->{$name} = $new;
         } elseif ($op->{$name} === $from) {
             $op->{$name} = $to;
             if ($op->isWriteVariable($name)) {
                 $to->addWriteOp($op);
             } else {
                 $to->addUsage($op);
             }
         }
     }
 }