public function leaveOp(Op $op, Block $block)
 {
     if (!$op instanceof Op\Expr\ConstFetch) {
         return null;
     }
     if (!$op->name instanceof Operand\Literal) {
         // Non-constant op
         return null;
     }
     $value = null;
     switch (strtolower($op->name->value)) {
         case 'true':
             $value = true;
             break;
         case 'false':
             $value = false;
             break;
         case 'null':
             $value = null;
             break;
         default:
             // TODO: try to lookup other constants at runtime
             return null;
     }
     $value = new Operand\Literal($value);
     $value->type = Type::fromValue($value->value);
     Helper::replaceVar($op->result, $value);
     return Visitor::REMOVE_OP;
 }
Пример #2
0
 public function infer(array $components)
 {
     $this->components = $components;
     $resolved = new \SplObjectStorage();
     $unresolved = new \SplObjectStorage();
     foreach ($components['variables'] as $op) {
         if (!empty($op->type) && $op->type->type !== Type::TYPE_UNKNOWN && $op->type->type !== Type::TYPE_MIXED) {
             $resolved[$op] = $op->type;
         } elseif ($op instanceof Operand\Literal) {
             $resolved[$op] = Type::fromValue($op->value);
         } else {
             $unresolved[$op] = Type::getAllPosibilities();
         }
     }
     if (count($unresolved) === 0) {
         // short-circuit
         return;
     }
     do {
         echo "Round " . $round++ . " (" . count($unresolved) . " unresolved variables out of " . count($components['variables']) . ")\n";
         $start = round(count($resolved) / count($unresolved), 6);
         $i = 0;
         $toRemove = [];
         foreach ($unresolved as $k => $var) {
             $i++;
             if ($i % 10 === 0) {
                 echo ".";
             }
             if ($i % 800 === 0) {
                 echo "\n";
             }
             $type = $this->resolveVar($var, $resolved);
             if ($type) {
                 $toRemove[] = $var;
                 $resolved[$var] = $type;
             }
         }
         foreach ($toRemove as $remove) {
             $unresolved->detach($remove);
         }
         echo "\n";
     } while (count($unresolved) > 0 && $start < round(count($resolved) / count($unresolved), 6));
     foreach ($resolved as $var) {
         $var->type = $resolved[$var];
     }
     foreach ($unresolved as $var) {
         $var->type = new Type(Type::TYPE_UNKNOWN);
     }
 }
 public function leaveOp(Op $op, Block $block)
 {
     if (!$op instanceof Op\Expr\BooleanNot) {
         return null;
     }
     if (!$op->expr instanceof Operand\Literal) {
         // Non-constant op
         return null;
     }
     $value = new Operand\Literal(-$op->expr->value);
     $value->type = Type::fromValue($newValue->value);
     Helper::replaceVar($op->result, $value);
     Helper::removeUsage($op->expr, $op);
     return Visitor::REMOVE_OP;
 }
Пример #4
0
 protected function verifyReturn($function, State $state)
 {
     if (!$function->stmts) {
         // interface
         return [];
     }
     $errors = [];
     if ($function->returnType) {
         $type = Type::fromDecl($function->returnType->value);
     } else {
         $type = Type::extractTypeFromComment("return", $function->getAttribute('doccomment'));
         if (Type::mixed()->equals($type)) {
             // only verify actual types
             return $errors;
         }
     }
     $returns = $this->findReturnBlocks($function->stmts);
     foreach ($returns as $return) {
         if (!$return || !$return->expr) {
             // Default return, no
             if ($type->allowsNull()) {
                 continue;
             }
             if (!$return) {
                 $errors[] = ["Default return found for non-null type {$type}", $function];
             } else {
                 $errors[] = ["Explicit null return found for non-null type {$type}", $return];
             }
         } elseif (!$return->expr->type) {
             var_dump($return->expr);
             $errors[] = ["Could not resolve type for return", $return];
         } else {
             if (!$state->resolver->resolves($return->expr->type, $type)) {
                 $errors[] = ["Type mismatch on return value, found {$return->expr->type} expecting {$type}", $return];
             }
         }
     }
     return $errors;
 }
Пример #5
0
 /**
  * @return array
  */
 protected function verifyInternalCall($func, $call, $state, $name)
 {
     $errors = [];
     foreach ($func['params'] as $idx => $param) {
         if (!isset($call->args[$idx])) {
             if (substr($param['name'], -1) !== '=') {
                 $errors[] = ["Missing required argument {$idx} for call {$name}()", $call];
             }
             continue;
         }
         if ($param['type'] && isset($call->args[$idx]->type)) {
             $type = Type::fromDecl($param['type']);
             if (is_string($call->args[$idx]->type)) {
                 $call->args[$idx]->type = Type::fromDecl($call->args[$idx]->type);
             }
             if (!$state->resolver->resolves($call->args[$idx]->type, $type)) {
                 $errors[] = ["Type mismatch on {$name}() argument {$idx}, found {$call->args[$idx]->type} expecting {$type}", $call];
             }
         }
     }
     return $errors;
 }
 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;
 }
Пример #7
0
 protected function processTypeAssertion(Assertion\TypeAssertion $assertion, Operand $source, SplObjectStorage $resolved)
 {
     if ($assertion->value instanceof Operand) {
         if ($assertion->value instanceof Operand\Literal) {
             return Type::fromDecl($assertion->value->value);
         }
         if (isset($resolved[$assertion->value])) {
             return $resolved[$assertion->value];
         }
         return false;
     }
     $subTypes = [];
     foreach ($assertion->value as $sub) {
         $subTypes[] = $subType = $this->processTypeAssertion($sub, $source, $resolved);
         if (!$subType) {
             // Not fully resolved yet
             return false;
         }
     }
     $type = $assertion->mode === Assertion::MODE_UNION ? Type::TYPE_UNION : Type::TYPE_INTERSECTION;
     return new Type($type, $subTypes);
 }