Compiles the class/interface contained in the file.

@param CompilationContext $compilationContext
@param string             $namespace
@param array              $classStatement
338084
public function compileClass(CompilationContext $compilationContext, $namespace, $classStatement)
    {
        $classDefinition = $this->classDefinition;

        /*
         * Do the compilation
         */
        $classDefinition->compile($compilationContext);
    }
548729
protected function writeDeclaration(ClassDeclaration $declaration, string $type = null)
    {
        $type = $type ?? static::ELEMENT;

        $filename = $this->config->classFilename($type, $this->argument('name'));
        $filename = $this->files->normalizePath($filename);

        if ($this->files->exists($filename)) {
            $this->writeln(
                "<fg=red>Unable to create '<comment>{$declaration->getName()}</comment>' declaration, "
                . "file '<comment>{$filename}</comment>' already exists.</fg=red>"
            );

            return;
        }

        //File declaration
        $file = new FileDeclaration(
            $this->config->classNamespace($type, $this->argument('name'))
        );

        $file->setComment($this->config->headerLines());
        $file->addElement($declaration);

        $this->files->write(
            $filename,
            $file->render(),
            FilesInterface::READONLY,
            true
        );

        $this->writeln(
            "Declaration of '<info>{$declaration->getName()}</info>' "
            . "has been successfully written into '<comment>{$filename}</comment>'."
        );
    }
1281293
protected function parseNamespaceDeclaration($namespaceDeclaration)
    {
        $result = preg_match(self::SPLIT_PATTERN_NAMESPACEDECLARATION, $namespaceDeclaration, $matches);
        if ($result !== 1 || !(isset($matches['alias']) && isset($matches['packageKey']))) {
            throw new Fusion\Exception('Invalid namespace declaration "' . $namespaceDeclaration . '"', 1180547190);
        }

        $namespaceAlias = $matches['alias'];
        $namespacePackageKey = $matches['packageKey'];
        $this->objectTypeNamespaces[$namespaceAlias] = $namespacePackageKey;
    }
1039632
protected function declareProperty(CompilationContext $compilationContext, $type, $value)
    {
        $codePrinter = $compilationContext->codePrinter;

        if (\is_object($value)) {
            return;
        }

        $classEntry = $compilationContext->classDefinition->getClassEntry();

        switch ($type) {
            case 'long':
            case 'int':
                $codePrinter->output('zend_declare_property_long('.$classEntry.', SL("'.$this->getName().'"), '.$value.', '.$this->getVisibilityAccessor().' TSRMLS_CC);');
                break;

            case 'double':
                $codePrinter->output('zend_declare_property_double('.$classEntry.', SL("'.$this->getName().'"), '.$value.', '.$this->getVisibilityAccessor().' TSRMLS_CC);');
                break;

            case 'bool':
                $codePrinter->output('zend_declare_property_bool('.$classEntry.', SL("'.$this->getName().'"), '.$this->getBooleanCode($value).', '.$this->getVisibilityAccessor().' TSRMLS_CC);');
                break;

            case Types::T_CHAR:
            case Types::T_STRING:
                $codePrinter->output(
                    sprintf(
                        'zend_declare_property_string(%s, SL("%s"), "%s", %s TSRMLS_CC);',
                        $classEntry,
                        $this->getName(),
                        add_slashes($value),
                        $this->getVisibilityAccessor()
                    )
                );
                break;

            case 'array':
            case 'empty-array':
            case 'null':
                $codePrinter->output('zend_declare_property_null('.$classEntry.', SL("'.$this->getName().'"), '.$this->getVisibilityAccessor().' TSRMLS_CC);');
                break;

            default:
                throw new CompilerException('Unknown default type: '.$type, $this->original);
        }
    }
1238835
private function _processInterfaceStatement() {
		// Check PHPDoc Presence
		$this->_checkDocExists(T_INTERFACE);

		$this->_ncssTotalInterfaces ++;
		$this->_ncssFileInterfaces ++;

		// Test if there is more than one class per file
		if ($this->_isActive('oneInterfacePerFile') && $this->_ncssFileInterfaces > 1) {
			$msg = $this->_getMessage('ONE_INTERFACE_PER_FILE', $this->_currentFilename);
			$this->_writeError('oneInterfacePerFile', $msg);
		}

		// Reset the default values
		$this->_inFunction = false;
		$this->_nbFunctionParameters = 0;
		$this->_functionParameters = array();
		$this->_inFunctionStatement = false;
		$this->_functionReturns = false;
		$this->_functionThrows = false;
		$this->_inControlStatement = false;
		$this->_currentStatement = false;
		$this->_inInterfaceStatement = true;

		// skip until T_STRING representing the interface name
		while (!$this->tokenizer->checkCurrentToken(T_STRING)) {
			$this->tokenizer->getNextToken();
		}

		$token = $this->tokenizer->getCurrentToken();
		$interfacename = $token->text;
		$this->_currentInterfacename = $interfacename;

		// Test that the interface name matches the file name
		$this->_checkTypeNameFileNameMatch($interfacename);

		// Check interface naming
		$this->_checkInterfaceNaming($interfacename);

		$this->_checkWhiteSpaceAfter($interfacename);
	}
445150
public function initNonReferenced(CompilationContext $compilationContext)
    {
        $compilationContext->headersManager->add('kernel/memory');
        $compilationContext->codePrinter->output('ZEPHIR_INIT_ZVAL_NREF('.$this->getName().');');
    }
1117386
public function get(CompilationContext $compilationContext, $method, $allowNtsCache = true)
    {
        if (!\is_object($method)) {
            return 'NULL, 0';
        }

        if (!($method instanceof \ReflectionMethod)) {
            $completeName = $method->getClassDefinition()->getCompleteName();

            /*
             * Avoid generate caches for external classes
             */
            if ($method->getClassDefinition()->isExternal()) {
                return 'NULL, 0';
            }

            if (isset($this->cache[$completeName][$method->getName()])) {
                return '&'.$this->cache[$completeName][$method->getName()]->getName().', '.SlotsCache::getExistingMethodSlot($method);
            }

            if ($method->getClassDefinition()->isInterface()) {
                return 'NULL, 0';
            }
        }

        $mustBeCached = false;
        if (!$compilationContext->insideCycle) {
            if (!($method instanceof \ReflectionMethod)) {
                $classDefinition = $method->getClassDefinition();
                if (!$classDefinition->isBundled() && $allowNtsCache) {
                    $mustBeCached = !$compilationContext->backend->isZE3();
                } else {
                    if (!$method->isPrivate() && !$method->isFinal()) {
                        return 'NULL, 0';
                    }
                }
            } else {
                if (!$method->isPrivate() && !$method->isFinal()) {
                    return 'NULL, 0';
                }
            }
        }

        $functionCache = $compilationContext->symbolTable->getTempVariableForWrite('zephir_fcall_cache_entry', $compilationContext);

        if ($method->isPrivate() || $method->isFinal() || $mustBeCached) {
            $cacheSlot = SlotsCache::getMethodSlot($method);
        } else {
            $cacheSlot = '0';
        }

        $functionCache->setMustInitNull(true);
        $functionCache->setReusable(false);

        if (!($method instanceof \ReflectionMethod)) {
            $this->cache[$completeName][$method->getName()] = $functionCache;
        }

        return '&'.$functionCache->getName().', '.$cacheSlot;
    }
931830
public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) {
		$allowedTokens = array(
			T_OPEN_PARENTHESIS,
			T_CLOSE_PARENTHESIS,
		);

		$next = $phpcsFile->findNext($allowedTokens, ($stackPtr + 1));
		
		if ($next === false) {
			$error = 'New object declaration must contain a couple parentheses: new MyClass()';
			$phpcsFile->addError($error, $stackPtr, 'ObjectParenthesisNotFound');
		}
	}
661600
public function checkStrictType(array $parameter, CompilationContext $compilationContext)
    {
        if (isset($parameter['data-type'])) {
            $dataType = $parameter['data-type'];
        } else {
            $dataType = 'variable';
        }

        $compilationContext->headersManager->add('ext/spl/spl_exceptions');
        $compilationContext->headersManager->add('kernel/exception');

        $codePrinter = new CodePrinter();
        $codePrinter->increaseLevel();
        $oldCodePrinter = $compilationContext->codePrinter;
        $compilationContext->codePrinter = $codePrinter;
        $compilationContext->backend->checkStrictType($dataType, $parameter, $compilationContext);

        $compilationContext->codePrinter = $oldCodePrinter;

        return $codePrinter->getOutput();
    }
253775
public function processValue(CompilationContext $compilationContext)
    {
        if ('constant' == $this->value['type']) {
            $constant = new Constants();
            $compiledExpression = $constant->compile($this->value, $compilationContext);

            $this->value = [
                'type' => $compiledExpression->getType(),
                'value' => $compiledExpression->getCode(),
            ];

            return;
        }

        if ('static-constant-access' == $this->value['type']) {
            $staticConstantAccess = new StaticConstantAccess();
            $compiledExpression = $staticConstantAccess->compile($this->value, $compilationContext);

            $this->value = [
                'type' => $compiledExpression->getType(),
                'value' => $compiledExpression->getCode(),
            ];

            return;
        }
    }
654768
public function addMethod(ClassMethod $method, $statement = null)
    {
        $methodName = strtolower($method->getName());
        if (isset($this->methods[$methodName])) {
            throw new CompilerException("Method '".$method->getName()."' was defined more than one time", $statement);
        }

        $this->methods[$methodName] = $method;
    }
287887
public function compile(Expression $expr, CompilationContext $compilationContext)
    {
        $this->expression = $expr;
        $expression = $expr->getExpression();

        switch ($expression['call-type']) {
            case self::CALL_NORMAL:
                return $this->_callNormal($expression, $compilationContext);
            case self::CALL_DYNAMIC:
                return $this->_callDynamic($expression, $compilationContext);
        }

        return new CompiledExpression('null', null, $expression);
    }
1042582
public function compile(CompilationContext $compilationContext)
    {
        $statement = $this->statement;

        $codePrinter = $compilationContext->codePrinter;

        if (isset($statement['expr'])) {
            $currentMethod = $compilationContext->currentMethod;

            if ($currentMethod->isConstructor()) {
                throw new CompilerException('Constructors cannot return values', $statement['expr']);
            }

            if ($currentMethod->isVoid()) {
                throw new CompilerException(
                    "Method is marked as 'void' and it must not return any value",
                    $statement['expr']
                );
            }

            /*
             * Use return member for properties on this
             */
            if ('property-access' == $statement['expr']['type']) {
                if ('variable' == $statement['expr']['left']['type']) {
                    if ('this' == $statement['expr']['left']['value']) {
                        if ('variable' == $statement['expr']['right']['type']) {
                            /*
                             * If the property is accessed on 'this', we check if the property does exist.
                             */
                            $property = $statement['expr']['right']['value'];
                            $classDefinition = $compilationContext->classDefinition;
                            if (!$classDefinition->hasProperty($property)) {
                                throw new CompilerException(
                                    sprintf(
                                        "Class '%s' does not have a property called: '%s'",
                                        $classDefinition->getCompleteName(),
                                        $property
                                    ),
                                    $statement['expr']['right']
                                );
                            }

                            $compilationContext->headersManager->add('kernel/object');
                            $codePrinter->output('RETURN_MM_MEMBER(getThis(), "'.$property.'");');

                            return;
                        }
                    }
                }
            }

            /*
             * Fetches return_value and tries to return the value directly there.
             */
            $variable = $compilationContext->symbolTable->getVariable('return_value');

            $expr = new Expression($statement['expr']);
            $expr->setExpectReturn(true, $variable);
            $expr->setReadOnly(true);
            $resolvedExpr = $expr->compile($compilationContext);

            /*
             * Here we check if the variable returns a compatible type according to its type hints
             */
            if ($currentMethod->hasReturnTypes()) {
                switch ($resolvedExpr->getType()) {
                    case Types::T_NULL:
                        if (false == $currentMethod->areReturnTypesNullCompatible()) {
                            throw new InvalidTypeException($resolvedExpr->getType(), $statement['expr']);
                        }
                        break;

                    case Types::T_INT:
                    case Types::T_UINT:
                    case Types::T_LONG:
                    case Types::T_ULONG:
                    case Types::T_CHAR:
                    case Types::T_UCHAR:
                        if (false == $currentMethod->areReturnTypesIntCompatible()) {
                            throw new InvalidTypeException($resolvedExpr->getType(), $statement['expr']);
                        }
                        break;

                    case Types::T_BOOL:
                        if (false == $currentMethod->areReturnTypesBoolCompatible()) {
                            throw new InvalidTypeException($resolvedExpr->getType(), $statement['expr']);
                        }
                        break;

                    case Types::T_DOUBLE:
                        if (false == $currentMethod->areReturnTypesDoubleCompatible()) {
                            throw new InvalidTypeException($resolvedExpr->getType(), $statement['expr']);
                        }
                        break;

                    case Types::T_STRING:
                    case Types::T_ISTRING:
                        if (false == $currentMethod->areReturnTypesStringCompatible()) {
                            throw new InvalidTypeException($resolvedExpr->getType(), $statement['expr']);
                        }
                        break;

                    case Types::T_VARIABLE:
                        $symbolVariable = $compilationContext->symbolTable->getVariableForRead(
                            $resolvedExpr->getCode(),
                            $compilationContext,
                            $statement['expr']
                        );

                        switch ($symbolVariable->getType()) {
                            case Types::T_INT:
                            case Types::T_UINT:
                            case Types::T_LONG:
                            case Types::T_ULONG:
                            case Types::T_CHAR:
                            case Types::T_UCHAR:
                                if (false == $currentMethod->areReturnTypesIntCompatible()) {
                                    throw new InvalidTypeException($resolvedExpr->getType(), $statement['expr']);
                                }
                                break;

                            case Types::T_DOUBLE:
                                if (false == $currentMethod->areReturnTypesDoubleCompatible()) {
                                    throw new InvalidTypeException($resolvedExpr->getType(), $statement['expr']);
                                }
                                break;

                            case Types::T_STRING:
                            case Types::T_ISTRING:
                                if (false == $currentMethod->areReturnTypesStringCompatible()) {
                                    throw new InvalidTypeException($resolvedExpr->getType(), $statement['expr']);
                                }
                                break;

                            case Types::T_BOOL:
                                if (false == $currentMethod->areReturnTypesBoolCompatible()) {
                                    throw new InvalidTypeException($resolvedExpr->getType(), $statement['expr']);
                                }
                                break;

                            case Types::T_VARIABLE:
                                break;
                        }
                        break;
                }
            }

            switch ($resolvedExpr->getType()) {
                case Types::T_NULL:
                    $codePrinter->output('RETURN_MM_NULL();');
                    break;

                case Types::T_INT:
                case Types::T_UINT:
                case Types::T_LONG:
                case Types::T_ULONG:
                case Types::T_CHAR:
                case Types::T_UCHAR:
                    $codePrinter->output('RETURN_MM_LONG('.$resolvedExpr->getCode().');');
                    break;

                case Types::T_BOOL:
                    $codePrinter->output('RETURN_MM_BOOL('.$resolvedExpr->getBooleanCode().');');
                    break;

                case Types::T_DOUBLE:
                    $codePrinter->output('RETURN_MM_DOUBLE('.$resolvedExpr->getCode().');');
                    break;

                case Types::T_STRING:
                case Types::T_ISTRING:
                    $compilationContext->backend->returnString(
                        add_slashes($resolvedExpr->getCode()),
                        $compilationContext
                    );
                    break;

                case Types::T_ARRAY:
                    if ('return_value' != $resolvedExpr->getCode()) {
                        $codePrinter->output('RETURN_CTOR('.$resolvedExpr->getCode().');');
                    } else {
                        $codePrinter->output('RETURN_MM();');
                    }
                    break;

                case Types::T_VARIABLE:
                    if (!isset($symbolVariable)) {
                        $symbolVariable = $compilationContext->symbolTable->getVariableForRead(
                            $resolvedExpr->getCode(),
                            $compilationContext,
                            $statement['expr']
                        );
                    }

                    switch ($symbolVariable->getType()) {
                        case Types::T_INT:
                        case Types::T_UINT:
                        case Types::T_LONG:
                        case Types::T_ULONG:
                        case Types::T_CHAR:
                        case Types::T_UCHAR:
                            $codePrinter->output('RETURN_MM_LONG('.$symbolVariable->getName().');');
                            break;

                        case Types::T_DOUBLE:
                            $codePrinter->output('RETURN_MM_DOUBLE('.$symbolVariable->getName().');');
                            break;

                        case Types::T_STRING:
                        case Types::T_ISTRING:
                        case Types::T_ARRAY:
                            $codePrinter->output(
                                'RETURN_CTOR('.$compilationContext->backend->getVariableCode($symbolVariable).');'
                            );
                            break;

                        case Types::T_BOOL:
                            $codePrinter->output('RETURN_MM_BOOL('.$symbolVariable->getName().');');
                            break;

                        case Types::T_VARIABLE:
                            if ('this_ptr' == $symbolVariable->getName()) {
                                $codePrinter->output('RETURN_THIS();');
                            } else {
                                if ('return_value' != $symbolVariable->getName()) {
                                    if (!$symbolVariable->isExternal()) {
                                        if ($symbolVariable->isLocalOnly()) {
                                            $codePrinter->output(
                                                sprintf(
                                                    'RETURN_LCTOR(%s);',
                                                    $compilationContext->backend->getVariableCode($symbolVariable)
                                                )
                                            );
                                        } else {
                                            if (!$symbolVariable->isMemoryTracked()) {
                                                $codePrinter->output(
                                                    sprintf(
                                                        'RETURN_CTOR(%s);',
                                                        $compilationContext->backend->getVariableCode($symbolVariable)
                                                    )
                                                );
                                            } else {
                                                $codePrinter->output(
                                                    sprintf(
                                                        'RETURN_CCTOR(%s);',
                                                        $compilationContext->backend->getVariableCode($symbolVariable)
                                                    )
                                                );
                                            }
                                        }
                                    } else {
                                        $codePrinter->output(
                                            sprintf(
                                                'RETVAL_ZVAL(%s, 1, 0);',
                                                $compilationContext->backend->getVariableCode($symbolVariable)
                                            )
                                        );
                                        $codePrinter->output('RETURN_MM();');
                                    }
                                } else {
                                    $codePrinter->output('RETURN_MM();');
                                }
                            }
                            if ($symbolVariable->isTemporal()) {
                                $symbolVariable->setIdle(true);
                            }
                            break;

                        default:
                            throw new CompilerException(
                                sprintf("Cannot return variable '%s'", $symbolVariable->getType()),
                                $statement['expr']
                            );
                    }
                    break;

                default:
                    throw new CompilerException("Cannot return '".$resolvedExpr->getType()."'", $statement['expr']);
            }

            return;
        }

        /*
         * Return without an expression
         */
        $codePrinter->output('RETURN_MM_NULL();');
    }
1079273
public function compile(CompilationContext $compilationContext)
    {
        $compilationContext->headersManager->add('kernel/array');
        $expression = $this->statement['expr'];

        $flags = 'PH_SEPARATE';

        if ('list' == $expression['type']) {
            $expression = $expression['left'];
        }

        switch ($expression['type']) {
            case 'array-access':
                $expr = new Expression($expression['left']);
                $expr->setReadOnly(true);
                $exprVar = $expr->compile($compilationContext);
                $variable = $compilationContext->symbolTable->getVariableForWrite($exprVar->getCode(), $compilationContext, $this->statement);

                $expr = new Expression($expression['right']);
                $expr->setReadOnly(true);
                $exprIndex = $expr->compile($compilationContext);
                break;

            case 'property-access':
                $expr = new Expression($expression['left']);
                $expr->setReadOnly(true);
                $exprVar = $expr->compile($compilationContext);
                $variable = $compilationContext->symbolTable->getVariableForWrite($exprVar->getCode(), $compilationContext, $this->statement);
                $variableCode = $compilationContext->backend->getVariableCode($variable);

                $compilationContext->headersManager->add('kernel/object');
                $compilationContext->codePrinter->output('zephir_unset_property('.$variableCode.', "'.$expression['right']['value'].'" TSRMLS_CC);');

                return true;

            case 'property-dynamic-access':
                //@todo fix it

            default:
                throw new CompilerException('Cannot use expression type: '.$expression['type'].' in "unset"', $expression);
        }

        if (!\in_array($variable->getType(), ['variable', 'array'])) {
            throw new CompilerException('Cannot use variable type: '.$variable->gettype().' in "unset"', $expression['left']);
        }

        if ($variable->hasDifferentDynamicType(['undefined', 'array', 'object', 'null'])) {
            $compilationContext->logger->warning(
                'Possible attempt to use non array/object in unset operator',
                ['non-valid-unset', $expression['left']]
            );
        }

        $compilationContext->backend->arrayUnset($variable, $exprIndex, $flags, $compilationContext);
    }
875043
public function compile(CompilationContext $compilationContext)
    {
        $compilationContext->headersManager->add('kernel/exception');

        $codePrinter = $compilationContext->codePrinter;
        $statement = $this->statement;
        $expr = $statement['expr'];

        /*
         * This optimizes throw new Exception("hello")
         */
        if (!$compilationContext->insideTryCatch) {
            if (isset($expr['class']) &&
                isset($expr['parameters']) &&
                1 == \count($expr['parameters']) &&
                'string' == $expr['parameters'][0]['parameter']['type']
            ) {
                $className = fqcn(
                    $expr['class'],
                    $compilationContext->classDefinition->getNamespace(),
                    $compilationContext->aliasManager
                );

                if ($compilationContext->compiler->isClass($className)) {
                    $classDefinition = $compilationContext->compiler->getClassDefinition($className);
                    $message = $expr['parameters'][0]['parameter']['value'];
                    try {
                        $class = $classDefinition->getClassEntry($compilationContext);
                        $this->throwStringException($codePrinter, $class, $message, $expr);

                        return;
                    } catch (Exception $e) {
                        throw new CompilerException($e->getMessage(), $expr, $e->getCode(), $e);
                    }
                } else {
                    if ($compilationContext->compiler->isBundledClass($className)) {
                        $classEntry = $compilationContext->classDefinition->getClassEntryByClassName(
                            $className,
                            $compilationContext,
                            true
                        );
                        if ($classEntry) {
                            $message = $expr['parameters'][0]['parameter']['value'];
                            $this->throwStringException($codePrinter, $classEntry, $message, $expr);

                            return;
                        }
                    }
                }
            } else {
                if (\in_array($expr['type'], ['string', 'char', 'int', 'double'])) {
                    $class = $compilationContext->classDefinition->getClassEntryByClassName(
                        'Exception',
                        $compilationContext
                    );

                    $this->throwStringException($codePrinter, $class, $expr['value'], $expr);

                    return;
                }
            }
        }

        try {
            $throwExpr = new Expression($expr);
            $resolvedExpr = $throwExpr->compile($compilationContext);
        } catch (Exception $e) {
            throw new CompilerException($e->getMessage(), $expr, $e->getCode(), $e);
        }

        if (!\in_array($resolvedExpr->getType(), ['variable', 'string'])) {
            throw new CompilerException(
                "Expression '".$resolvedExpr->getType().'" cannot be used as exception',
                $expr
            );
        }

        $variableVariable = $compilationContext->symbolTable->getVariableForRead(
            $resolvedExpr->getCode(),
            $compilationContext,
            $expr
        );

        if (!\in_array($variableVariable->getType(), ['variable', 'string'])) {
            throw new CompilerException(
                "Variable '".$variableVariable->getType()."' cannot be used as exception",
                $expr
            );
        }

        $variableCode = $compilationContext->backend->getVariableCode($variableVariable);
        $file = Compiler::getShortUserPath($statement['expr']['file']);
        $line = $statement['expr']['line'];

        $codePrinter->output(
            'zephir_throw_exception_debug('.$variableCode.', "'.$file.'", '.$line.' TSRMLS_CC);'
        );

        if (!$compilationContext->insideTryCatch) {
            $codePrinter->output('ZEPHIR_MM_RESTORE();');
            $codePrinter->output('return;');
        } else {
            $codePrinter->output('goto try_end_'.$compilationContext->currentTryCatch.';');
            $codePrinter->outputBlankLine();
        }

        if ($variableVariable->isTemporal()) {
            $variableVariable->setIdle(true);
        }
    }
936398
public function compile(CompilationContext $compilationContext)
    {
        $expression = $this->expression;
        $type = $expression['type'];
        $compilableExpression = null;

        switch ($type) {
            case 'null':
                return new LiteralCompiledExpression('null', null, $expression);

            case 'int':
            case 'integer':
                return new LiteralCompiledExpression('int', $expression['value'], $expression);

            case 'long':
            case 'double':
            case 'bool':
                return new LiteralCompiledExpression($type, $expression['value'], $expression);

            case 'string':
                return new LiteralCompiledExpression('string', str_replace(PHP_EOL, '\\n', $expression['value']), $expression);
            case 'istring':
                return new LiteralCompiledExpression('istring', str_replace(PHP_EOL, '\\n', $expression['value']), $expression);

            case 'char':
                if (!\strlen($expression['value'])) {
                    throw new CompilerException('Invalid empty char literal', $expression);
                }
                if (\strlen($expression['value']) > 2) {
                    if (\strlen($expression['value']) > 10) {
                        throw new CompilerException("Invalid char literal: '".substr($expression['value'], 0, 10)."...'", $expression);
                    } else {
                        throw new CompilerException("Invalid char literal: '".$expression['value']."'", $expression);
                    }
                }

                return new LiteralCompiledExpression('char', $expression['value'], $expression);

            case 'variable':
                $var = $compilationContext->symbolTable->getVariable($expression['value']);
                if ($var) {
                    if ('this' == $var->getRealName()) {
                        $var = 'this';
                    } else {
                        $var = $var->getName();
                    }
                } else {
                    $var = $expression['value'];
                }

                return new CompiledExpression('variable', $var, $expression);

            case 'constant':
                $compilableExpression = new Constants();
                break;

            case 'empty-array':
                return $this->emptyArray($expression, $compilationContext);

            case 'array-access':
                $compilableExpression = new NativeArrayAccess();
                $compilableExpression->setNoisy($this->isNoisy());
                break;

            case 'property-access':
                $compilableExpression = new PropertyAccess();
                $compilableExpression->setNoisy($this->isNoisy());
                break;

            case 'property-string-access':
            case 'property-dynamic-access':
                $compilableExpression = new PropertyDynamicAccess();
                $compilableExpression->setNoisy($this->isNoisy());
                break;

            case 'static-constant-access':
                $compilableExpression = new StaticConstantAccess();
                break;

            case 'static-property-access':
                $compilableExpression = new StaticPropertyAccess();
                break;

            case 'fcall':
                $functionCall = new FunctionCall();

                return $functionCall->compile($this, $compilationContext);

            case 'mcall':
                $methodCall = new MethodCall();

                return $methodCall->compile($this, $compilationContext);

            case 'scall':
                $staticCall = new StaticCall();

                return $staticCall->compile($this, $compilationContext);

            case 'isset':
                $compilableExpression = new IssetOperator();
                break;

            case 'fetch':
                $compilableExpression = new FetchOperator();
                break;

            case 'empty':
                $compilableExpression = new EmptyOperator();
                break;

            case 'array':
                $compilableExpression = new NativeArray();
                break;

            case 'new':
                $compilableExpression = new NewInstanceOperator();
                break;

            case 'new-type':
                $compilableExpression = new NewInstanceTypeOperator();
                break;

            case 'not':
                $compilableExpression = new NotOperator();
                break;

            case 'bitwise_not':
                $compilableExpression = new BitwiseNotOperator();
                break;

            case 'equals':
                $compilableExpression = new EqualsOperator();
                break;

            case 'not-equals':
                $compilableExpression = new NotEqualsOperator();
                break;

            case 'identical':
                $compilableExpression = new IdenticalOperator();
                break;

            case 'not-identical':
                $compilableExpression = new NotIdenticalOperator();
                break;

            case 'greater':
                $compilableExpression = new GreaterOperator();
                break;

            case 'less':
                $compilableExpression = new LessOperator();
                break;

            case 'less-equal':
                $compilableExpression = new LessEqualOperator();
                break;

            case 'greater-equal':
                $compilableExpression = new GreaterEqualOperator();
                break;

            case 'add':
                $compilableExpression = new AddOperator();
                break;

            case 'minus':
                $compilableExpression = new MinusOperator();
                break;

            case 'sub':
                $compilableExpression = new SubOperator();
                break;

            case 'mul':
                $compilableExpression = new MulOperator();
                break;

            case 'div':
                $compilableExpression = new DivOperator();
                break;

            case 'mod':
                $compilableExpression = new ModOperator();
                break;

            case 'and':
                $compilableExpression = new AndOperator();
                break;

            case 'or':
                $compilableExpression = new OrOperator();
                break;

            case 'bitwise_and':
                $compilableExpression = new BitwiseAndOperator();
                break;

            case 'bitwise_or':
                $compilableExpression = new BitwiseOrOperator();
                break;

            case 'bitwise_xor':
                $compilableExpression = new BitwiseXorOperator();
                break;

            case 'bitwise_shiftleft':
                $compilableExpression = new ShiftLeftOperator();
                break;

            case 'bitwise_shiftright':
                $compilableExpression = new ShiftRightOperator();
                break;

            case 'concat':
                $expr = new ConcatOperator();
                $expr->setExpectReturn($this->expecting, $this->expectingVariable);

                return $expr->compile($expression, $compilationContext);

            case 'irange':
                $compilableExpression = new RangeInclusiveOperator();
                break;

            case 'erange':
                $compilableExpression = new RangeExclusiveOperator();
                break;

            case 'list':
                if ('list' == $expression['left']['type']) {
                    $compilationContext->logger->warning(
                        'Unnecessary extra parentheses',
                        ['extra-parentheses', $expression]
                    );
                }
                $numberPrints = $compilationContext->codePrinter->getNumberPrints();
                $expr = new self($expression['left']);
                $expr->setExpectReturn($this->expecting, $this->expectingVariable);
                $resolved = $expr->compile($compilationContext);
                if (($compilationContext->codePrinter->getNumberPrints() - $numberPrints) <= 1) {
                    if (false !== strpos($resolved->getCode(), ' ')) {
                        return new CompiledExpression($resolved->getType(), '('.$resolved->getCode().')', $expression);
                    }
                }

                return $resolved;

            case 'cast':
                $compilableExpression = new CastOperator();
                break;

            case 'type-hint':
                $expr = new TypeHintOperator();
                $expr->setReadOnly($this->isReadOnly());
                $expr->setExpectReturn($this->expecting, $this->expectingVariable);

                return $expr->compile($expression, $compilationContext);

            case 'instanceof':
                $compilableExpression = new InstanceOfOperator();
                break;

            case 'clone':
                $compilableExpression = new CloneOperator();
                break;

            case 'ternary':
                $compilableExpression = new TernaryOperator();
                break;

            case 'short-ternary':
                $expr = new ShortTernaryOperator();
                $expr->setReadOnly($this->isReadOnly());
                $expr->setExpectReturn($this->expecting, $this->expectingVariable);

                return $expr->compile($expression, $compilationContext);

            case 'likely':
                if (!$this->evalMode) {
                    throw new CompilerException("'likely' operator can only be used in evaluation expressions", $expression);
                }
                $expr = new LikelyOperator();
                $expr->setReadOnly($this->isReadOnly());

                return $expr->compile($expression, $compilationContext);

            case 'unlikely':
                if (!$this->evalMode) {
                    throw new CompilerException("'unlikely' operator can only be used in evaluation expressions", $expression);
                }
                $expr = new UnlikelyOperator();
                $expr->setReadOnly($this->isReadOnly());

                return $expr->compile($expression, $compilationContext);

            case 'typeof':
                $compilableExpression = new TypeOfOperator();
                break;

            case 'require':
                $compilableExpression = new RequireOperator();
                break;

            case 'closure':
                $compilableExpression = new Closure();
                break;

            case 'closure-arrow':
                $compilableExpression = new ClosureArrow();
                break;

            case 'reference':
                $compilableExpression = new Reference();
                break;

            default:
                throw new CompilerException('Unknown expression: '.$type, $expression);
        }

        if (!$compilableExpression) {
            throw new CompilerException('Unknown expression passed as compilableExpression', $expression);
        }
        $compilableExpression->setReadOnly($this->isReadOnly());
        $compilableExpression->setExpectReturn($this->expecting, $this->expectingVariable);

        return $compilableExpression->compile($expression, $compilationContext);
    }
424005
public function compile(CompilationContext $compilationContext)
    {
        /*
         * Set the method currently being compiled
         */
        $compilationContext->currentMethod = $this;

        /*
         * Initialize the method warm-up to null
         */
        $compilationContext->methodWarmUp = null;

        /**
         * Assign pre-made compilation passes.
         */
        $localContext = $this->localContext;
        $typeInference = $this->typeInference;
        $callGathererPass = $this->callGathererPass;

        /**
         * Initialization of parameters happens in a fictitious external branch.
         */
        $branch = new Branch();
        $branch->setType(Branch::TYPE_EXTERNAL);
        /**
         * BranchManager helps to create graphs of conditional/loop/root/jump branches.
         */
        $branchManager = new BranchManager();
        $branchManager->addBranch($branch);

        $compilationContext->branchManager = $branchManager;

        /**
         * Every method has its own symbol table.
         */
        $symbolTable = new SymbolTable($compilationContext);
        if ($localContext) {
            $symbolTable->setLocalContext($localContext);
        }

        if ($this->staticVariables) {
            foreach ($this->staticVariables as $var) {
                $localVar = clone $var;
                $localVar->setIsExternal(true);
                $localVar->setLocalOnly(true);
                $localVar->setType('variable');
                $symbolTable->addRawVariable($localVar);
            }
        }

        /**
         * Parameters has an additional extra mutation.
         */
        $parameters = $this->parameters;
        if ($localContext) {
            if (\is_object($parameters)) {
                foreach ($parameters->getParameters() as $parameter) {
                    $localContext->increaseMutations($parameter['name']);
                }
            }
        }

        /**
         * Cache Manager manages function calls, method calls and class entries caches.
         */
        $cacheManager = new CacheManager();
        $cacheManager->setGatherer($callGathererPass);

        $compilationContext->cacheManager = $cacheManager;
        $compilationContext->typeInference = $typeInference;
        $compilationContext->symbolTable = $symbolTable;

        $oldCodePrinter = $compilationContext->codePrinter;

        /**
         * Change the code printer to a single method instance.
         */
        $codePrinter = new CodePrinter();
        $compilationContext->codePrinter = $codePrinter;

        /*
         * Set an empty function cache
         */
        $compilationContext->functionCache = null;

        /*
         * Reset try/catch and loop counter
         */
        $compilationContext->insideCycle = 0;
        $compilationContext->insideTryCatch = 0;
        $compilationContext->currentTryCatch = 0;

        if (\is_object($parameters)) {
            /**
             * Round 1. Create variables in parameters in the symbol table.
             */
            $classCastChecks = [];
            $substituteVars = [];
            foreach ($parameters->getParameters() as $parameter) {
                /*
                 * Change dynamic variables to low level types
                 */
                if ($typeInference) {
                    if (isset($parameter['data-type'])) {
                        if ('variable' == $parameter['data-type']) {
                            $type = $typeInference->getInferedType($parameter['name']);
                            if (\is_string($type)) {
                                /* promote polymorphic parameters to low level types */
                            }
                        }
                    } else {
                        $type = $typeInference->getInferedType($parameter['name']);
                        if (\is_string($type)) {
                            /* promote polymorphic parameters to low level types */
                        }
                    }
                }

                $symbolParam = null;

                if (isset($parameter['data-type'])) {
                    switch ($parameter['data-type']) {
                        case 'object':
                        case 'callable':
                        case 'resource':
                        case 'variable':
                            $symbol = $symbolTable->addVariable($parameter['data-type'], $parameter['name'], $compilationContext);
                            /* TODO: Move this to the respective backend, which requires refactoring how this works */
                            if ($compilationContext->backend->isZE3()) {
                                $symbol->setIsDoublePointer(true);
                                $substituteVars[$parameter['name']] = $symbolTable->addVariable('variable', $parameter['name'].'_sub', $compilationContext);
                            }
                            break;

                        default:
                            $symbol = $symbolTable->addVariable($parameter['data-type'], $parameter['name'], $compilationContext);
                            $symbolParam = $symbolTable->addVariable('variable', $parameter['name'].'_param', $compilationContext);
                            /* TODO: Move this to the respective backend, which requires refactoring how this works */
                            if ($compilationContext->backend->isZE3()) {
                                $symbolParam->setIsDoublePointer(true);
                            }
                            if ('string' == $parameter['data-type'] || 'array' == $parameter['data-type']) {
                                $symbol->setMustInitNull(true);
                            }
                            break;
                    }
                } else {
                    $symbol = $symbolTable->addVariable('variable', $parameter['name'], $compilationContext);
                }

                /* ZE3 only */
                if (isset($substituteVars[$parameter['name']])) {
                    $substituteVar = $substituteVars[$parameter['name']];
                    $substituteVar->increaseUses();
                }

                /*
                 * Some parameters can be read-only
                 */
                if (isset($parameter['const']) && $parameter['const']) {
                    $symbol->setReadOnly(true);
                    if (\is_object($symbolParam)) {
                        $symbolParam->setReadOnly(true);
                    }
                }

                if (\is_object($symbolParam)) {
                    /*
                     * Parameters are marked as 'external'
                     */
                    $symbolParam->setIsExternal(true);

                    /*
                     * Assuming they're initialized
                     */
                    $symbolParam->setIsInitialized(true, $compilationContext);

                    /*
                     * Initialize auxiliary parameter zvals to null
                     */
                    $symbolParam->setMustInitNull(true);

                    /*
                     * Increase uses
                     */
                    $symbolParam->increaseUses();
                } else {
                    if (isset($parameter['default'])) {
                        if (isset($parameter['data-type'])) {
                            if ('variable' == $parameter['data-type']) {
                                $symbol->setMustInitNull(true);
                            }
                        } else {
                            $symbol->setMustInitNull(true);
                        }
                    }
                }

                /*
                 * Original node where the variable was declared
                 */
                $symbol->setOriginal($parameter);

                /*
                 * Parameters are marked as 'external'
                 */
                $symbol->setIsExternal(true);

                /*
                 * Assuming they're initialized
                 */
                $symbol->setIsInitialized(true, $compilationContext);

                /*
                 * Variables with class/type must be objects across the execution
                 */
                if (isset($parameter['cast'])) {
                    $symbol->setDynamicTypes('object');
                    $symbol->setClassTypes($compilationContext->getFullName($parameter['cast']['value']));
                    $classCastChecks[] = [$symbol, $parameter];
                } else {
                    if (isset($parameter['data-type'])) {
                        if ('variable' == $parameter['data-type']) {
                            $symbol->setDynamicTypes('undefined');
                        }
                    } else {
                        $symbol->setDynamicTypes('undefined');
                    }
                }
            }
        }

        $compilationContext->backend->onPreCompile($this, $compilationContext);

        /*
         * Compile the block of statements if any
         */
        if (\is_object($this->statements)) {
            if ($this->hasModifier('static')) {
                $compilationContext->staticContext = true;
            } else {
                $compilationContext->staticContext = false;
            }

            /*
             * Compile the statements block as a 'root' branch
             */
            $this->statements->compile($compilationContext, false, Branch::TYPE_ROOT);
        }

        /**
         * Initialize variable default values.
         */
        $initVarCode = $compilationContext->backend->initializeVariableDefaults($symbolTable->getVariables(), $compilationContext);

        /**
         * Fetch parameters from vm-top.
         */
        $initCode = '';
        $code = '';
        if (\is_object($parameters)) {
            /**
             * Round 2. Fetch the parameters in the method.
             */
            $params = [];
            $requiredParams = [];
            $optionalParams = [];
            $numberRequiredParams = 0;
            $numberOptionalParams = 0;
            foreach ($parameters->getParameters() as $parameter) {
                if (isset($parameter['data-type'])) {
                    $dataType = $parameter['data-type'];
                } else {
                    $dataType = 'variable';
                }

                switch ($dataType) {
                    case 'object':
                    case 'callable':
                    case 'resource':
                    case 'variable':
                        if (!$this->isInternal()) {
                            $params[] = '&'.$parameter['name'];
                        } else {
                            $params[] = $parameter['name'];
                        }
                        break;

                    default:
                        if (!$this->isInternal()) {
                            $params[] = '&'.$parameter['name'].'_param';
                        } else {
                            $params[] = $parameter['name'].'_param';
                        }
                        break;
                }

                if (isset($parameter['default'])) {
                    $optionalParams[] = $parameter;
                    ++$numberOptionalParams;
                } else {
                    $requiredParams[] = $parameter;
                    ++$numberRequiredParams;
                }
            }

            /**
             * Pass the write detector to the method statement block to check if the parameter
             * variable is modified so as do the proper separation.
             */
            $parametersToSeparate = [];
            if (\is_object($this->statements)) {
                /*
                 * If local context is not available
                 */
                if (!$localContext) {
                    $writeDetector = new WriteDetector();
                }

                foreach ($parameters->getParameters() as $parameter) {
                    if (isset($parameter['data-type'])) {
                        $dataType = $parameter['data-type'];
                    } else {
                        $dataType = 'variable';
                    }

                    switch ($dataType) {
                        case 'variable':
                        case 'string':
                        case 'array':
                        case 'resource':
                        case 'object':
                        case 'callable':
                            $name = $parameter['name'];
                            if (!$localContext) {
                                if ($writeDetector->detect($name, $this->statements->getStatements())) {
                                    $parametersToSeparate[$name] = true;
                                }
                            } else {
                                if ($localContext->getNumberOfMutations($name) > 1) {
                                    $parametersToSeparate[$name] = true;
                                }
                            }
                            break;
                    }
                }
            }

            /*
             * Initialize required parameters
             */
            foreach ($requiredParams as $parameter) {
                if (isset($parameter['mandatory'])) {
                    $mandatory = $parameter['mandatory'];
                } else {
                    $mandatory = 0;
                }

                if (isset($parameter['data-type'])) {
                    $dataType = $parameter['data-type'];
                } else {
                    $dataType = 'variable';
                }

                if ('variable' != $dataType) {
                    /*
                     * Assign value from zval to low level type
                     */
                    if ($mandatory) {
                        $initCode .= $this->checkStrictType($parameter, $compilationContext);
                    } else {
                        $initCode .= $this->assignZvalValue($parameter, $compilationContext);
                    }
                }

                switch ($dataType) {
                    case 'variable':
                    case 'resource':
                    case 'object':
                    case 'callable':
                        if (isset($parametersToSeparate[$parameter['name']])) {
                            $symbolTable->mustGrownStack(true);
                            $initCode .= "\t".'ZEPHIR_SEPARATE_PARAM('.$parameter['name'].');'.PHP_EOL;
                        }
                        break;
                }
            }

            /*
             * Initialize optional parameters
             */
            foreach ($optionalParams as $parameter) {
                if (isset($parameter['mandatory'])) {
                    $mandatory = $parameter['mandatory'];
                } else {
                    $mandatory = 0;
                }

                if (isset($parameter['data-type'])) {
                    $dataType = $parameter['data-type'];
                } else {
                    $dataType = 'variable';
                }

                switch ($dataType) {
                    case 'object':
                    case 'callable':
                    case 'resource':
                    case 'variable':
                        $name = $parameter['name'];
                        break;

                    default:
                        $name = $parameter['name'].'_param';
                        break;
                }

                /**
                 * Assign the default value according to the variable's type.
                 */
                $targetVar = $compilationContext->symbolTable->getVariableForWrite($name, $compilationContext);
                $initCode .= "\t".$compilationContext->backend->ifVariableValueUndefined($targetVar, $compilationContext, false, false).PHP_EOL;

                if ($compilationContext->backend->isZE3()) {
                    if ($targetVar->isDoublePointer() && isset($substituteVars[$parameter['name']])) {
                        $substituteVar = $substituteVars[$parameter['name']];
                        $initCode .= "\t\t".$targetVar->getName().' = &'.$substituteVar->getName().';'.PHP_EOL;
                    }
                }
                $initCode .= $this->assignDefaultValue($parameter, $compilationContext);

                if (isset($parametersToSeparate[$name]) || 'variable' != $dataType) {
                    $initCode .= "\t".'} else {'.PHP_EOL;
                    if (isset($parametersToSeparate[$name])) {
                        $initCode .= "\t\t".'ZEPHIR_SEPARATE_PARAM('.$name.');'.PHP_EOL;
                    } else {
                        if ($mandatory) {
                            $initCode .= $this->checkStrictType($parameter, $compilationContext, $mandatory);
                        } else {
                            $initCode .= "\t".$this->assignZvalValue($parameter, $compilationContext);
                        }
                    }
                }

                $initCode .= "\t".'}'.PHP_EOL;
            }

            /*
             * Fetch the parameters to zval pointers
             */
            $codePrinter->preOutputBlankLine();

            if (!$this->isInternal()) {
                $compilationContext->headersManager->add('kernel/memory');
                if ($symbolTable->getMustGrownStack()) {
                    $code .= "\t".'zephir_fetch_params(1, '.$numberRequiredParams.', '.$numberOptionalParams.', '.implode(', ', $params).');'.PHP_EOL;
                } else {
                    $code .= "\t".'zephir_fetch_params(0, '.$numberRequiredParams.', '.$numberOptionalParams.', '.implode(', ', $params).');'.PHP_EOL;
                }
            } else {
                foreach ($params as $param) {
                    /* TODO: Migrate all this code to codeprinter, get rid of temp code printer */
                    $tempCodePrinter = new CodePrinter();
                    $realCodePrinter = $compilationContext->codePrinter;
                    $compilationContext->codePrinter = $tempCodePrinter;
                    $paramVar = $compilationContext->symbolTable->getVariableForRead($param, $compilationContext);
                    $compilationContext->backend->assignZval($paramVar, $param.'_ext', $compilationContext);
                    $code .= "\t".$tempCodePrinter->getOutput().PHP_EOL;
                    $compilationContext->codePrinter = $realCodePrinter;
                }
            }
            $code .= PHP_EOL;
        }

        $code .= $initCode.$initVarCode;
        $codePrinter->preOutput($code);

        $compilationContext->headersManager->add('kernel/object');
        /*
         * Fetch used superglobals
         */
        foreach ($symbolTable->getVariables() as $name => $variable) {
            if ($variable->isSuperGlobal()) {
                $globalVar = $symbolTable->getVariable($name);
                $codePrinter->preOutput("\t".$compilationContext->backend->fetchGlobal($globalVar, $compilationContext, false));
            }
            if ($variable->isLocalSatic()) {
                $staticVar = $symbolTable->getVariable($name);

                $codePrinter->preOutput(sprintf(
                    "\t".'zephir_read_static_property_ce(%s%s, %s, SL("%s"), PH_NOISY_CC%s);',
                    $staticVar->isDoublePointer() ? '' : '&',
                    $staticVar->getName(),
                    $this->classDefinition->getClassEntry(),
                    $staticVar->getName(),
                    ''
                ));
            }
        }

        /*
         * Grow the stack if needed
         */
        if ($symbolTable->getMustGrownStack()) {
            $compilationContext->headersManager->add('kernel/memory');
            $codePrinter->preOutput("\t".'ZEPHIR_MM_GROW();');
        }

        /**
         * Check if there are unused variables.
         */
        $usedVariables = [];
        $classDefinition = $this->getClassDefinition();
        if ($classDefinition) {
            $completeName = $classDefinition->getCompleteName();
        } else {
            $completeName = '[unknown]';
        }

        foreach ($symbolTable->getVariables() as $variable) {
            if ($variable->getNumberUses() <= 0) {
                if (false == $variable->isExternal()) {
                    $compilationContext->logger->warning(
                        'Variable "'.$variable->getName().'" declared but not used in '.$completeName.'::'.$this->getName(),
                        ['unused-variable', $variable->getOriginal()]
                    );
                    continue;
                }
                $compilationContext->logger->warning(
                    'Variable "'.$variable->getName().'" declared but not used in '.$completeName.'::'.$this->getName(),
                    ['unused-variable-external', $variable->getOriginal()]
                );
            }

            if ('this_ptr' != $variable->getName() && 'return_value' != $variable->getName() && 'return_value_ptr' != $variable->getName()) {
                $type = $variable->getType();
                if (!isset($usedVariables[$type])) {
                    $usedVariables[$type] = [];
                }
                $usedVariables[$type][] = $variable;
            }
        }

        /*
         * Check if there are assigned but not used variables
         * Warn whenever a variable is unused aside from its declaration.
         */
        foreach ($symbolTable->getVariables() as $variable) {
            if (true == $variable->isExternal() || $variable->isTemporal()) {
                continue;
            }

            if ('this_ptr' == $variable->getName() || 'return_value' == $variable->getName() || 'return_value_ptr' == $variable->getName() || 'ZEPHIR_LAST_CALL_STATUS' == $variable->getName()) {
                continue;
            }

            if (!$variable->isUsed()) {
                $node = $variable->getLastUsedNode();
                if (\is_array($node)) {
                    $expression = isset($node['expr']) ? $node['expr'] : $node;
                    $compilationContext->logger->warning(
                        'Variable "'.$variable->getName().'" assigned but not used in '.$completeName.'::'.$this->getName(),
                        ['unused-variable', $expression]
                    );
                } else {
                    $compilationContext->logger->warning(
                        'Variable "'.$variable->getName().'" assigned but not used in '.$completeName.'::'.$this->getName(),
                        ['unused-variable', $variable->getOriginal()]
                    );
                }
            }
        }

        if (\count($usedVariables)) {
            $codePrinter->preOutputBlankLine();
        }

        /**
         * Generate the variable definition for variables used.
         */
        $initCode = "\t".implode(PHP_EOL."\t", $compilationContext->backend->declareVariables($this, $usedVariables, $compilationContext));
        if ($initCode) {
            $codePrinter->preOutput($initCode);
        }

        /*
         * Finalize the method compilation
         */
        if (\is_object($this->statements)) {
            /**
             * If the last statement is not a 'return' or 'throw' we need to
             * restore the memory stack if needed.
             */
            $lastType = $this->statements->getLastStatementType();

            if ('return' != $lastType && 'throw' != $lastType && !$this->hasChildReturnStatementType($this->statements->getLastStatement())) {
                if ($symbolTable->getMustGrownStack()) {
                    $compilationContext->headersManager->add('kernel/memory');
                    $codePrinter->output("\t".'ZEPHIR_MM_RESTORE();');
                }

                /*
                 * If a method has return-type hints we need to ensure the last statement is a 'return' statement
                 */
                if ($this->hasReturnTypes()) {
                    throw new CompilerException(
                        'Reached end of the method without returning a valid type specified in the return-type hints',
                        $this->expression['return-type']
                    );
                }
            }
        }

        $compilationContext->backend->onPostCompile($this, $compilationContext);

        /**
         * Remove macros that grow/restore the memory frame stack if it wasn't used.
         */
        $code = $this->removeMemoryStackReferences($symbolTable, $codePrinter->getOutput());

        /*
         * Restore the compilation context
         */
        $oldCodePrinter->output($code);
        $compilationContext->codePrinter = $oldCodePrinter;

        $compilationContext->branchManager = null;
        $compilationContext->cacheManager = null;
        $compilationContext->typeInference = null;

        $codePrinter->clear();

        return null;
    }