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; }
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; }
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; }
/** * @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; }
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); }