Resolves an item to be added in an array.

@param CompiledExpression $exprCompiled
@param CompilationContext $compilationContext

@return Variable
499772
public function getArrayValue(CompiledExpression $exprCompiled, CompilationContext $compilationContext)
    {
        switch ($exprCompiled->getType()) {
            case 'int':
            case 'uint':
            case 'long':
                $tempVar = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext);
                $compilationContext->backend->assignLong($tempVar, $exprCompiled->getCode(), $compilationContext);

                return $tempVar;

            case 'char':
            case 'uchar':
                $tempVar = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext);
                $compilationContext->backend->assignLong($tempVar, '\''.$exprCompiled->getCode().'\'', $compilationContext);

                return $tempVar;

            case 'double':
                $tempVar = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext);
                $compilationContext->backend->assignDouble($tempVar, $exprCompiled->getCode(), $compilationContext);

                return $tempVar;

            case 'bool':
                if ('true' == $exprCompiled->getCode()) {
                    return new GlobalConstant('ZEPHIR_GLOBAL(global_true)');
                }

                if ('false' == $exprCompiled->getCode()) {
                    return new GlobalConstant('ZEPHIR_GLOBAL(global_false)');
                }

                $tempVar = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext);
                $compilationContext->backend->assignBool($tempVar, $exprCompiled->getCode(), $compilationContext);

                return $tempVar;

            case 'null':
                return new GlobalConstant('ZEPHIR_GLOBAL(global_null)');

            case 'string':
            case 'ulong':
                $tempVar = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext);
                $compilationContext->backend->assignString($tempVar, $exprCompiled->getCode(), $compilationContext);

                return $tempVar;

            case 'array':
                return $compilationContext->symbolTable->getVariableForRead($exprCompiled->getCode(), $compilationContext, $exprCompiled->getOriginal());

            case 'variable':
                $itemVariable = $compilationContext->symbolTable->getVariableForRead($exprCompiled->getCode(), $compilationContext, $exprCompiled->getOriginal());
                switch ($itemVariable->getType()) {
                    case 'int':
                    case 'uint':
                    case 'long':
                    case 'ulong':
                        $tempVar = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext);
                        $compilationContext->backend->assignLong($tempVar, $itemVariable, $compilationContext);

                        return $tempVar;

                    case 'double':
                        $tempVar = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext);
                        $compilationContext->backend->assignDouble($tempVar, $itemVariable, $compilationContext);

                        return $tempVar;

                    case 'bool':
                        $tempVar = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext);
                        $compilationContext->backend->assignBool($tempVar, $itemVariable, $compilationContext);

                        return $tempVar;

                    case 'string':
                    case 'variable':
                    case 'array':
                        return $itemVariable;

                    default:
                        throw new CompilerException('Unknown '.$itemVariable->getType(), $itemVariable);
                }
                break;

            default:
                throw new CompilerException('Unknown', $exprCompiled);
        }
    }
65287
private function _getOptimizedConcat($expression, CompilationContext $compilationContext, &$isFullString)
    {
        $originalExpr = $expression;

        $isFullString = true;

        $parts = [];
        while ($expression && isset($expression['left'])) {
            $parts[] = $expression['right'];
            if ('concat' == $expression['left']['type']) {
                $expression = $expression['left'];
            } else {
                $parts[] = $expression['left'];
                $expression = null;
            }
        }
        if ($expression) {
            $parts[] = $expression['right'];
            $parts[] = $expression['left'];
        }

        $key = '';
        $concatParts = [];
        $parts = array_reverse($parts);
        foreach ($parts as $part) {
            $expr = new Expression($part);
            $expr->setStringOperation(true);
            switch ($part['type']) {
                case 'array-access':
                case 'property-access':
                    $expr->setReadOnly(true);
                    break;

                default:
                    $expr->setReadOnly($this->readOnly);
                    break;
            }

            $compiledExpr = $expr->compile($compilationContext);
            switch ($compiledExpr->getType()) {
                case 'variable':
                    $variable = $compilationContext->symbolTable->getVariableForRead($compiledExpr->getCode(), $compilationContext, $originalExpr);
                    switch ($variable->getType()) {
                        case 'variable':
                            $key .= 'v';
                            $concatParts[] = $compilationContext->backend->getVariableCode($variable);
                            $isFullString = false;
                            break;

                        case 'string':
                            $key .= 'v';
                            $concatParts[] = $compilationContext->backend->getVariableCode($variable);
                            break;

                        case 'int':
                        case 'long':
                            $key .= 'v';
                            $tempVariable = $compilationContext->symbolTable->getTempLocalVariableForWrite('variable', $compilationContext, $originalExpr);
                            $compilationContext->backend->assignLong($tempVariable, $compiledExpr->getCode(), $compilationContext);
                            $concatParts[] = $compilationContext->backend->getVariableCode($tempVariable);
                            break;

                        default:
                            throw new CompilerException('Variable type: '.$variable->getType().' cannot be used in concat operation', $compiledExpr->getOriginal());
                    }
                    break;

                case 'string':
                    $key .= 's';
                    $concatParts[] = '"'.add_slashes($compiledExpr->getCode()).'"';
                    break;

                case 'int':
                case 'long':
                    $key .= 'v';
                    $tempVariable = $compilationContext->symbolTable->getTempLocalVariableForWrite('variable', $compilationContext, $originalExpr);
                    $compilationContext->codePrinter->output('ZVAL_LONG(&'.$tempVariable->getName().', '.$compiledExpr->getCode().');');
                    $concatParts[] = '&'.$tempVariable->getName();
                    break;

                default:
                    throw new CompilerException('Variable type: '.$compiledExpr->getType().' cannot be used in concat operation', $compiledExpr->getOriginal());
            }
        }

        $compilationContext->stringsManager->addConcatKey($key);

        return [$key, implode(', ', $concatParts)];
    }
619682
public function compile(CompilationContext $compilationContext)
    {
        if ($compilationContext->insideCycle) {
            $compilationContext->codePrinter->output('continue;');
        } else {
            throw new CompilerException("Cannot use 'continue' outside of a loop", $this->statement);
        }
    }
1096895
private function createMatches(array $expression, CompilationContext $context)
    {
        if (isset($expression['parameters'][2])) {
            $type = $expression['parameters'][2]['parameter']['type'];

            if ('variable' !== $type) {
                throw new CompilerException('Only variables can be passed by reference', $expression);
            }

            $name = $expression['parameters'][2]['parameter']['value'];

            if (!$variable = $context->symbolTable->getVariable($name)) {
                throw new CompilerException(
                    sprintf("Cannot mutate variable '%s' because it wasn't defined", $name),
                    $expression
                );
            }

            if (!\in_array($variable->getType(), ['variable', 'array'], true)) {
                throw new CompilerException(
                    sprintf(
                        "The '%s' variable must be either a variable or an array, got %s",
                        $name,
                        $variable->getType()
                    ),
                    $expression
                );
            }

            if (false == $variable->isInitialized()) {
                $variable->initVariant($context);
                $variable->setIsInitialized(true, $context);
            }
        } else {
            $variable = $context->symbolTable->addTemp('variable', $context);
            $variable->initVariant($context);
        }

        $variable->setDynamicTypes('array');

        return $variable;
    }
1129180
protected function _callDynamic(array $expression, CompilationContext $compilationContext)
    {
        $variable = $compilationContext->symbolTable->getVariableForRead(
            $expression['name'],
            $compilationContext,
            $expression
        );

        switch ($variable->getType()) {
            case 'variable':
            case 'string':
                break;

            default:
                throw new CompilerException(
                    'Variable type: '.$variable->getType().' cannot be used as dynamic caller',
                    $expression['left']
                );
        }

        /*
         * Resolve parameters
         */
        if (isset($expression['parameters'])) {
            $params = $this->getResolvedParams($expression['parameters'], $compilationContext, $expression);
        } else {
            $params = [];
        }

        $codePrinter = $compilationContext->codePrinter;

        /*
         * Process the expected symbol to be returned
         */
        $this->processExpectedObservedReturn($compilationContext);

        /**
         * At this point the function will be called in the PHP userland.
         * PHP functions only return zvals so we need to validate the target variable is also a zval.
         */
        $symbolVariable = $this->getSymbolVariable();
        if ($symbolVariable) {
            if (!$symbolVariable->isVariable()) {
                throw new CompilerException(
                    'Returned values by functions can only be assigned to variant variables',
                    $expression
                );
            }

            /*
             * We don't know the exact dynamic type returned by the method call
             */
            $symbolVariable->setDynamicTypes('undefined');
        }

        /*
         * Include fcall header
         */
        $compilationContext->headersManager->add('kernel/fcall');

        /*
         * Add the last call status to the current symbol table
         */
        $this->addCallStatusFlag($compilationContext);

        /*
         * Call functions must grown the stack
         */
        $compilationContext->symbolTable->mustGrownStack(true);

        if (!isset($expression['parameters'])) {
            if ($this->isExpectingReturn()) {
                if ('return_value' != $symbolVariable->getName()) {
                    if ($this->mustInitSymbolVariable()) {
                        $symbolVariable->setMustInitNull(true);
                        $symbolVariable->trackVariant($compilationContext);
                    }
                }
                $compilationContext->backend->callDynamicFunction($symbolVariable, $variable, $compilationContext);
            } else {
                $compilationContext->backend->callDynamicFunction(null, $variable, $compilationContext);
            }
        } else {
            if (\count($params)) {
                if ($this->isExpectingReturn()) {
                    if ('return_value' != $symbolVariable->getName()) {
                        if ($this->mustInitSymbolVariable()) {
                            $symbolVariable->setMustInitNull(true);
                            $symbolVariable->trackVariant($compilationContext);
                        }
                    }
                    $compilationContext->backend->callDynamicFunction(
                        $symbolVariable,
                        $variable,
                        $compilationContext,
                        $params
                    );
                } else {
                    $compilationContext->backend->callDynamicFunction(null, $variable, $compilationContext, $params);
                }
            } else {
                if ($this->isExpectingReturn()) {
                    if ('return_value' != $symbolVariable->getName()) {
                        if ($this->mustInitSymbolVariable()) {
                            $symbolVariable->setMustInitNull(true);
                            $symbolVariable->trackVariant($compilationContext);
                        }
                    }
                    $compilationContext->backend->callDynamicFunction($symbolVariable, $variable, $compilationContext);
                } else {
                    $compilationContext->backend->callDynamicFunction(null, $variable, $compilationContext);
                }
            }
        }

        /*
         * Temporary variables must be copied if they have more than one reference
         */
        foreach ($this->getMustCheckForCopyVariables() as $checkVariable) {
            $codePrinter->output('zephir_check_temp_parameter('.$checkVariable.');');
        }

        $this->addCallStatusOrJump($compilationContext);

        /*
         * We can mark temporary variables generated as idle
         */
        foreach ($this->getTemporalVariables() as $tempVariable) {
            $tempVariable->setIdle(true);
        }

        if ($this->isExpectingReturn()) {
            return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression);
        }

        return new CompiledExpression('null', null, $expression);
    }
1359362
public function emptyArray($expression, CompilationContext $compilationContext)
    {
        /*
         * Resolves the symbol that expects the value
         */
        if ($this->expecting) {
            if ($this->expectingVariable) {
                $symbolVariable = &$this->expectingVariable;
                $symbolVariable->initVariant($compilationContext);
            } else {
                $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $expression);
            }
        } else {
            $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $expression);
        }

        /*
         * Variable that receives property accesses must be polymorphic
         */
        if (!$symbolVariable->isVariable() && 'array' != $symbolVariable->getType()) {
            throw new CompilerException('Cannot use variable: '.$symbolVariable->getName().'('.$symbolVariable->getType().') to create empty array', $expression);
        }

        /*
         * Mark the variable as an 'array'
         */
        $symbolVariable->setDynamicTypes('array');

        $compilationContext->backend->initArray($symbolVariable, $compilationContext);

        return new CompiledExpression('array', $symbolVariable->getRealName(), $expression);
    }
426291
public function processExpectedReturn(CompilationContext $compilationContext)
    {
        $expr = $this->expression;
        $expression = $expr->getExpression();

        /**
         * Create temporary variable if needed.
         */
        $mustInit = false;
        $symbolVariable = null;
        $isExpecting = $expr->isExpectingReturn();
        if ($isExpecting) {
            $symbolVariable = $expr->getExpectingVariable();
            if (\is_object($symbolVariable)) {
                $readDetector = new ReadDetector();
                if ($readDetector->detect($symbolVariable->getName(), $expression)) {
                    $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite(
                        'variable',
                        $compilationContext,
                        $expression
                    );
                } else {
                    $mustInit = true;
                }
            } else {
                $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $expression);
            }
        }

        $this->mustInit = $mustInit;
        $this->symbolVariable = $symbolVariable;
        $this->isExpecting = $isExpecting;
    }
346490
public function dispatch(Expression $expr)
    {
        switch (true) {
            case $expr instanceof Comparison:
                return $this->walkComparison($expr);
            case $expr instanceof Value:
                return $this->walkValue($expr);
            case $expr instanceof CompositeExpression:
                return $this->walkCompositeExpression($expr);
            default:
                throw new RuntimeException('Unknown Expression ' . get_class($expr));
        }
    }
60436
public function compile($expression, CompilationContext $compilationContext)
    {
        /**
         * Resolve the left part of the expression.
         */
        $expr = new Expression($expression['left']);
        $expr->setReadOnly(true);
        $exprVariable = $expr->compile($compilationContext);

        /*
         * Only dynamic variables can be used as arrays
         */
        switch ($exprVariable->getType()) {
            case 'variable':
                $variableVariable = $compilationContext->symbolTable->getVariableForRead($exprVariable->getCode(), $compilationContext, $expression);
                switch ($variableVariable->getType()) {
                    case 'variable':
                    case 'array':
                    case 'string':
                        break;

                    default:
                        throw new CompilerException('Variable type: '.$variableVariable->getType().' cannot be used as array', $expression['left']);
                }
                break;

            default:
                throw new CompilerException('Cannot use expression: '.$exprVariable->getType().' as an array', $expression['left']);
        }

        /*
         * Resolve the dimension according to variable's type
         */
        switch ($variableVariable->getType()) {
            case 'variable':
                return $this->_accessDimensionArray($expression, $variableVariable, $compilationContext);

            case 'array':
                return $this->_accessDimensionArray($expression, $variableVariable, $compilationContext);

            case 'string':
                return $this->_accessStringOffset($expression, $variableVariable, $compilationContext);
        }
    }
1178847
public function optimize(array $expression, Call $call, CompilationContext $context)
    {
        if (!isset($expression['parameters']) || \count($expression['parameters']) < 2) {
            throw new CompilerException(
                sprintf(
                    'preg_match() expects at least 2 parameters, %d given',
                    isset($expression['parameters']) ? \count($expression['parameters']) : 0
                ),
                $expression
            );
        }

        if (\count($expression['parameters']) > 5) {
            throw new CompilerException(
                sprintf(
                    'preg_match() expects at most 5 parameters, %d given',
                    \count($expression['parameters'])
                ),
                $expression
            );
        }

        $matches = $this->createMatches($expression, $context);
        list($flags, $offset) = $this->processOptionals($expression, $call, $context);

        /*
         * Process the expected symbol to be returned
         */
        $call->processExpectedReturn($context);

        $symbolVariable = $call->getSymbolVariable(true, $context);
        if (!$symbolVariable->isVariable()) {
            throw new CompilerException(
                'Returned values by functions can only be assigned to variant variables',
                $expression
            );
        }

        // zephir_preg_match
        $context->headersManager->add('kernel/string');

        try {
            $resolvedParams = $call->getReadOnlyResolvedParams($expression['parameters'], $context, $expression);

            if ($call->mustInitSymbolVariable()) {
                $symbolVariable->initVariant($context);
            }

            $symbol = $context->backend->getVariableCode($symbolVariable);
            $matchesSymbol = $context->backend->getVariableCode($matches);

            $context->codePrinter->output(
                sprintf(
                    'zephir_preg_match(%s, %s, %s, %s, %d, %s, %s TSRMLS_CC);',
                    $symbol,
                    $resolvedParams[0],
                    $resolvedParams[1],
                    $matchesSymbol,
                    static::GLOBAL_MATCH,
                    $flags,
                    $offset
                )
            );

            return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression);
        } catch (Exception $e) {
            throw new CompilerException($e->getMessage(), $expression, $e->getCode(), $e);
        }
    }
498304
public function compile(CompilationContext $compilationContext)
    {
        $compilationContext->codePrinter->output('while (1) {');

        /*
         * Variables are initialized in a different way inside cycle
         */
        ++$compilationContext->insideCycle;

        /*
         * Compile statements in the 'loop' block
         */
        if (!isset($this->statement['statements'])) {
            throw new CompilerException("Infinite loop without at least a 'break' statement is not allowed", $this->statement);
        }

        $st = new StatementsBlock($this->statement['statements']);

        /**
         * Check if the block contain at least a break statement.
         */
        $loopBreakPass = new LoopBreakPass();
        $loopBreakPass->pass($st);
        if (!$loopBreakPass->hasBreak()) {
            throw new CompilerException("Infinite loop without at least a 'break' statement is not allowed", $this->statement);
        }

        $st->isLoop(true);
        $st->compile($compilationContext, false, Branch::TYPE_LOOP_INFINITE);

        --$compilationContext->insideCycle;

        $compilationContext->codePrinter->output('}');
    }
633721
public function compile($expression, CompilationContext $compilationContext)
    {
        $className = $expression['left']['value'];
        $compiler = $compilationContext->compiler;
        $property = $expression['right']['value'];

        /*
         * Fetch the class definition according to the class where the constant
         * is supposed to be declared
         */
        if (!\in_array($className, ['self', 'static', 'parent'])) {
            $className = $compilationContext->getFullName($className);
            if ($compiler->isClass($className)) {
                $classDefinition = $compiler->getClassDefinition($className);
            } else {
                if ($compiler->isBundledClass($className)) {
                    $classDefinition = $compiler->getInternalClassDefinition($className);
                } else {
                    throw new CompilerException("Cannot locate class '".$className."'", $expression['left']);
                }
            }
        } else {
            if (\in_array($className, ['self', 'static'])) {
                $classDefinition = $compilationContext->classDefinition;
            } else {
                if ('parent' == $className) {
                    $classDefinition = $compilationContext->classDefinition;
                    $extendsClass = $classDefinition->getExtendsClass();
                    if (!$extendsClass) {
                        throw new CompilerException('Cannot access static property "'.$property.'" on parent because class '.$classDefinition->getCompleteName().' does not extend any class', $expression);
                    } else {
                        $classDefinition = $classDefinition->getExtendsClassDefinition();
                    }
                }
            }
        }

        if (!$classDefinition->hasProperty($property)) {
            throw new CompilerException("Class '".$classDefinition->getCompleteName()."' does not have a property called: '".$property."'", $expression);
        }

        /** @var ClassProperty $propertyDefinition */
        $propertyDefinition = $classDefinition->getProperty($property);
        if (!$propertyDefinition->isStatic()) {
            throw new CompilerException("Cannot access non-static property '".$classDefinition->getCompleteName().'::'.$property."'", $expression);
        }

        if ($propertyDefinition->isPrivate()) {
            if ($classDefinition != $compilationContext->classDefinition) {
                throw new CompilerException("Cannot access private static property '".$classDefinition->getCompleteName().'::'.$property."' out of its declaring context", $expression);
            }
        }

        if ($propertyDefinition->isProtected()) {
            if ($classDefinition != $compilationContext->classDefinition && $classDefinition != $compilationContext->classDefinition->getExtendsClassDefinition()) {
                throw new CompilerException("Cannot access protected static property '".$classDefinition->getCompleteName().'::'.$property."' out of its declaring context", $expression);
            }
        }

        /*
         * Resolves the symbol that expects the value
         */
        if ($this->expecting) {
            if ($this->expectingVariable) {
                $symbolVariable = $this->expectingVariable;
                if ('return_value' == $symbolVariable->getName()) {
                    $symbolVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext);
                }
            } else {
                $symbolVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext);
            }
        } else {
            $symbolVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext);
        }

        /*
         * Variable that receives property accesses must be polymorphic
         */
        if (!$symbolVariable->isVariable()) {
            throw new CompilerException('Cannot use variable: '.$symbolVariable->getType().' to assign class constants', $expression);
        }

        $symbolVariable->setDynamicTypes('undefined');

        $compilationContext->headersManager->add('kernel/object');

        $readOnly = $this->readOnly;
        if (!$readOnly) {
            if ('return_value' != $symbolVariable->getName()) {
                $symbolVariable->observeVariant($compilationContext);
            }
        }
        $compilationContext->backend->fetchStaticProperty($symbolVariable, $classDefinition, $property, $this->readOnly, $compilationContext);

        return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression);
    }
1047057
public void updateExpression(String expr, Object value, PageContext pageContext)
            throws ExpressionUpdateException, JspException
    {
        if (isExpression(expr)) {

            VariableResolver vr = ImplicitObjectUtil.getUpdateVariableResolver(pageContext.getRequest(), pageContext.getResponse(), false);
            ExpressionEvaluatorFactory.getInstance().update(expr, value, vr, false);
        }
        else {
            String s = Bundle.getString("Tags_BindingUpdateExpressionError", new Object[]{expr});
            _tag.registerTagError(s, null);
        }
    }
49569
public function compileRange($exprRaw, CompilationContext $compilationContext)
    {
        if (!\count($exprRaw['parameters'])) {
            return false;
        }

        if (\count($exprRaw['parameters']) > 3) {
            return false;
        }

        $functionCall = new FunctionCall();
        $parameters = $functionCall->getResolvedParamsAsExpr($exprRaw['parameters'], $compilationContext, $exprRaw);

        if (2 != \count($parameters) && 3 != \count($parameters)) {
            throw new CompilerException('Wrong number of parameters', $this->statement['expr']);
        }

        if ('variable' != $parameters[0]->getType()) {
            if (!$parameters[0]->isIntCompatibleType()) {
                return false;
            }
        }

        if ('variable' != $parameters[1]->getType()) {
            if (!$parameters[1]->isIntCompatibleType()) {
                return false;
            }
        }

        $codePrinter = $compilationContext->codePrinter;
        $exprBuilder = BuilderFactory::getInstance();

        /*
         * Initialize 'key' variable
         */
        if (isset($this->statement['key'])) {
            /**
             * This variable is used to check if the loop is in its first iteration.
             */
            $keyVariable = $compilationContext->symbolTable->getTempVariableForWrite('long', $compilationContext, $this->statement);
            $keyVariable->increaseUses();
        }

        /**
         * This variable is used to check if the loop is in its first iteration.
         */
        $flagVariable = $compilationContext->symbolTable->getTempVariableForWrite('bool', $compilationContext, $this->statement);

        if ('variable' != $parameters[0]->getType()) {
            $tempVariable = $compilationContext->symbolTable->addTemp($parameters[0]->getType(), $compilationContext);
        } else {
            $rangeVariable = $compilationContext->symbolTable->getVariableForRead($parameters[0]->getCode(), $compilationContext, $this->statement['expr']);
            $tempVariable = $compilationContext->symbolTable->addTemp($rangeVariable->getType(), $compilationContext);
        }

        /*
         * Create a copy of the current value in the end of the range to avoid modify the range
         * inside the cycle
         */
        if ('variable' != $parameters[1]->getType()) {
            $upperBoundVariable = $compilationContext->symbolTable->getTempVariable($parameters[1]->getType(), $compilationContext);
        } else {
            $rangeVariable = $compilationContext->symbolTable->getVariableForRead($parameters[1]->getCode(), $compilationContext, $this->statement['expr']);
            $upperBoundVariable = $compilationContext->symbolTable->getTempVariable($rangeVariable->getType(), $compilationContext);
        }

        /**
         * Create an implicit 'let' operation to set the current value in the upper bound of the range.
         */
        $builderLet = $exprBuilder->statements()->let();
        $builderLet->setAssignments([$exprBuilder->operators()
            ->assignVariable($upperBoundVariable->getName(), $exprBuilder->literal($parameters[1]->getType(), $parameters[1]->getCode())
                ->setFile($this->statement['file'])
                ->setLine($this->statement['line'])
                ->setChar($this->statement['char']))
            ->setFile($this->statement['file'])
            ->setLine($this->statement['line'])
            ->setChar($this->statement['char']),
        ]);

        $statement = new LetStatement($builderLet->build());
        $statement->compile($compilationContext);

        if ($this->statement['reverse']) {
            /*
             * Create an implicit 'let' operation for the initialize expression
             */
            $builderLet->setAssignments([$exprBuilder->operators()
                ->assignVariable($tempVariable->getName(), $exprBuilder->variable($upperBoundVariable->getName())
                    ->setFile($this->statement['file'])
                    ->setLine($this->statement['line'])
                    ->setChar($this->statement['char']))
                ->setFile($this->statement['file'])
                ->setLine($this->statement['line'])
                ->setChar($this->statement['char']),
            ]);
            $statement = new LetStatement($builderLet->build());
        } else {
            /*
             * Create an implicit 'let' operation for the initialize expression
             */
            $builderLet->setAssignments([$exprBuilder->operators()
                ->assignVariable($tempVariable->getName(), $exprBuilder->literal($parameters[0]->getType(), $parameters[0]->getCode())
                    ->setFile($this->statement['file'])
                    ->setLine($this->statement['line'])
                    ->setChar($this->statement['char']))
                ->setFile($this->statement['file'])
                ->setLine($this->statement['line'])
                ->setChar($this->statement['char']),
            ]);
            $statement = new LetStatement($builderLet->build());
        }

        $statement->compile($compilationContext);

        /*
         * Initialize 'key' variable
         */
        if (isset($this->statement['key'])) {
            $codePrinter->output($keyVariable->getName().' = 0;');
        }
        $codePrinter->output($flagVariable->getName().' = 0;');

        if ($this->statement['reverse']) {
            $conditionExpr = [
                'type' => 'greater-equal',
                'left' => ['type' => 'variable', 'value' => $tempVariable->getName()],
                'right' => ['type' => $parameters[0]->getType(), 'value' => $parameters[0]->getCode()],
            ];
        } else {
            $conditionExpr = [
                'type' => 'less-equal',
                'left' => ['type' => 'variable', 'value' => $tempVariable->getName()],
                'right' => ['type' => 'variable', 'value' => $upperBoundVariable->getName()],
            ];
        }

        $expr = new EvalExpression();
        $condition = $expr->optimize($conditionExpr, $compilationContext);

        $codePrinter->output('if ('.$condition.') {');
        $codePrinter->increaseLevel();

        /*
         * Inside a cycle
         */
        ++$compilationContext->insideCycle;

        $codePrinter->output('while (1) {');
        $codePrinter->increaseLevel();

        $codePrinter->output('if ('.$flagVariable->getName().') {');

        $codePrinter->increaseLevel();

        if (isset($this->statement['key'])) {
            $codePrinter->output($keyVariable->getName().'++;');
        }

        if ($this->statement['reverse']) {
            if (!isset($parameters[2])) {
                $statement = new LetStatement([
                    'type' => 'let',
                    'assignments' => [
                        [
                            'assign-type' => 'decr',
                            'variable' => $tempVariable->getName(),
                            'file' => $this->statement['file'],
                            'line' => $this->statement['line'],
                            'char' => $this->statement['char'],
                        ],
                    ],
                ]);
            } else {
                $statement = new LetStatement([
                    'type' => 'let',
                    'assignments' => [
                        [
                            'assign-type' => 'variable',
                            'operator' => 'sub-assign',
                            'variable' => $tempVariable->getName(),
                            'expr' => [
                                'type' => $parameters[2]->getType(),
                                'value' => $parameters[2]->getCode(),
                                'file' => $this->statement['file'],
                                'line' => $this->statement['line'],
                                'char' => $this->statement['char'],
                            ],
                            'file' => $this->statement['file'],
                            'line' => $this->statement['line'],
                            'char' => $this->statement['char'],
                        ],
                    ],
                ]);
            }
        } else {
            if (!isset($parameters[2])) {
                $statement = new LetStatement([
                    'type' => 'let',
                    'assignments' => [
                        [
                            'assign-type' => 'incr',
                            'variable' => $tempVariable->getName(),
                            'file' => $this->statement['file'],
                            'line' => $this->statement['line'],
                            'char' => $this->statement['char'],
                        ],
                    ],
                ]);
            } else {
                $statement = new LetStatement([
                    'type' => 'let',
                    'assignments' => [
                        [
                            'assign-type' => 'variable',
                            'operator' => 'add-assign',
                            'variable' => $tempVariable->getName(),
                            'expr' => [
                                'type' => $parameters[2]->getType(),
                                'value' => $parameters[2]->getCode(),
                                'file' => $this->statement['file'],
                                'line' => $this->statement['line'],
                                'char' => $this->statement['char'],
                            ],
                            'file' => $this->statement['file'],
                            'line' => $this->statement['line'],
                            'char' => $this->statement['char'],
                        ],
                    ],
                ]);
            }
        }
        $statement->compile($compilationContext);

        /**
         * Multi-line conditions would need to be regenerated here.
         */
        $condition = $expr->optimize($conditionExpr, $compilationContext);
        $codePrinter->output('if (!('.$condition.')) {');
        $codePrinter->output("\t".'break;');
        $codePrinter->output('}');

        $codePrinter->decreaseLevel();

        $codePrinter->output('} else {');
        $codePrinter->output("\t".$flagVariable->getName().' = 1;');
        $codePrinter->output('}');

        /*
         * Initialize 'key' variable
         */
        if (isset($this->statement['key'])) {
            /*
             * Check for anonymous variables
             */
            if ('_' != $this->statement['key']) {
                $keyVariableName = $this->statement['key'];
            } else {
                $keyVariableName = $keyVariable->getName();
            }

            /**
             * Create an implicit 'let' operation, @TODO use a builder.
             */
            $statement = new LetStatement([
                'type' => 'let',
                'assignments' => [
                    [
                        'assign-type' => 'variable',
                        'variable' => $keyVariableName,
                        'operator' => 'assign',
                        'expr' => [
                            'type' => 'variable',
                            'value' => $keyVariable->getName(),
                            'file' => $this->statement['file'],
                            'line' => $this->statement['line'],
                            'char' => $this->statement['char'],
                        ],
                        'file' => $this->statement['file'],
                        'line' => $this->statement['line'],
                        'char' => $this->statement['char'],
                    ],
                ],
            ]);

            $statement->compile($compilationContext);
        }

        /*
         * Initialize 'value' variable
         */
        if (isset($this->statement['value'])) {
            /*
             * Check for anonymous variables
             */
            if ('_' != $this->statement['value']) {
                $valueVariable = $this->statement['value'];
            } else {
                $valueVariable = $tempVariable->getName();
            }

            /**
             * Create an implicit 'let' operation, @TODO use a builder.
             */
            $statement = new LetStatement([
                'type' => 'let',
                'assignments' => [
                    [
                        'assign-type' => 'variable',
                        'variable' => $valueVariable,
                        'operator' => 'assign',
                        'expr' => [
                            'type' => 'variable',
                            'value' => $tempVariable->getName(),
                            'file' => $this->statement['file'],
                            'line' => $this->statement['line'],
                            'char' => $this->statement['char'],
                        ],
                        'file' => $this->statement['file'],
                        'line' => $this->statement['line'],
                        'char' => $this->statement['char'],
                    ],
                ],
            ]);

            $statement->compile($compilationContext);
        }

        $codePrinter->decreaseLevel();

        /*
         * Compile statements in the 'for' block
         */
        if (isset($this->statement['statements'])) {
            $st = new StatementsBlock($this->statement['statements']);
            $st->isLoop(true);
            if (isset($this->statement['key'])) {
                $st->getMutateGatherer()->increaseMutations($this->statement['key']);
            }
            $st->getMutateGatherer()->increaseMutations($this->statement['value']);
            $st->compile($compilationContext);
        }

        /*
         * Restore the cycle counter
         */
        --$compilationContext->insideCycle;

        $codePrinter->output('}');

        $codePrinter->decreaseLevel();

        $codePrinter->output('}');
    }
958242
public function compile(array $expression, CompilationContext $compilationContext)
    {
        $codePrinter = $compilationContext->codePrinter;

        /*
         * Resolves the symbol that expects the value
         */
        $this->literalOnly = false;
        $symbolVariable = $this->getExpectedNonLiteral($compilationContext, $expression);
        if (!$symbolVariable->isVariable()) {
            throw new CompilerException('Objects can only be instantiated into dynamic variables', $expression);
        }

        if ($symbolVariable->isLocalOnly()) {
            throw new CompilerException('Cannot use non-heap variable to store new instance', $expression);
        }

        if ('return_value' != $symbolVariable->getName()) {
            if ($symbolVariable->hasDifferentDynamicType(['unknown', 'undefined', 'object', 'null'])) {
                $compilationContext->logger->warning(
                    'Possible attempt to use non-object in "new" operator',
                    ['non-valid-new', $expression]
                );
            }
        }

        /*
         * Mark variables as dynamic objects
         */
        $symbolVariable->setDynamicTypes('object');

        $dynamic = false;
        if ('self' == $expression['class'] || 'static' == $expression['class']) {
            $className = $compilationContext->classDefinition->getCompleteName();
        } else {
            $className = $expression['class'];
            $dynamic = $expression['dynamic'];
            if (!$dynamic) {
                $className = $compilationContext->getFullName($expression['class']);
            }
        }

        if (!$className) {
            throw new CompilerException('A class name is required to instantiate the object', $expression);
        }

        /**
         * stdclass doesn't have constructors.
         */
        $lowerClassName = strtolower($className);
        $isStdClass = 'stdclass' === $lowerClassName || '\stdclass' === $lowerClassName;

        if ($isStdClass) {
            if (isset($expression['parameters']) && \count($expression['parameters']) > 0) {
                throw new CompilerException('stdclass does not receive parameters in its constructor', $expression);
            }

            $compilationContext->backend->initObject($symbolVariable, null, $compilationContext);
            $symbolVariable->setClassTypes('stdclass');
        } else {
            $classDefinition = false;
            if ($compilationContext->compiler->isClass($className)) {
                $classDefinition = $compilationContext->compiler->getClassDefinition($className);
            }

            /*
             * Classes inside the same extension
             */
            if ($classDefinition) {
                $compilationContext->backend->initObject($symbolVariable, $classDefinition->getClassEntry($compilationContext), $compilationContext);
                $symbolVariable->setClassTypes($className);
                $symbolVariable->setAssociatedClass($classDefinition);
            } else {
                /*
                 * Classes outside the extension
                 */
                if ($dynamic) {
                    $classNameVariable = $compilationContext->symbolTable->getVariableForRead($className, $compilationContext, $expression);
                    if ($classNameVariable->isNotVariableAndString()) {
                        throw new CompilerException('Only dynamic/string variables can be used in new operator. '.$classNameVariable->getName(), $expression);
                    }

                    /*
                     * Use a safe string version of the variable to avoid segfaults
                     */
                    $compilationContext->headersManager->add('kernel/object');
                    $safeSymbolVariable = $compilationContext->symbolTable->getTempVariable('variable', $compilationContext);
                    $safeSymbolVariable->setMustInitNull(true);
                    $safeSymbolVariable->setIsInitialized(true, $compilationContext);
                    $safeSymbolVariable->increaseUses();
                    $safeSymbol = $compilationContext->backend->getVariableCode($safeSymbolVariable);
                    $classNameSymbol = $compilationContext->backend->getVariableCode($classNameVariable);

                    $compilationContext->codePrinter->output('zephir_fetch_safe_class('.$safeSymbol.', '.$classNameSymbol.');');
                    $classNameToFetch = 'Z_STRVAL_P('.$safeSymbol.'), Z_STRLEN_P('.$safeSymbol.')';
                    $zendClassEntry = $compilationContext->cacheManager->getClassEntryCache()->get($classNameToFetch, true, $compilationContext);
                    $classEntry = $zendClassEntry->getName();
                } else {
                    if (!class_exists($className, false)) {
                        $compilationContext->logger->warning(
                            'Class "'.$className.'" does not exist at compile time',
                            ['nonexistent-class', $expression]
                        );
                        $classNameToFetch = 'SL("'.escape_class($className).'")';

                        $zendClassEntry = $compilationContext->cacheManager->getClassEntryCache()->get($classNameToFetch, false, $compilationContext);
                        $classEntry = $zendClassEntry->getName();
                    } else {
                        $reflectionClass = new \ReflectionClass($className);
                        if ($reflectionClass->isInterface()) {
                            throw new CompilerException('Interfaces cannot be instantiated', $expression);
                        } else {
                            if (method_exists($reflectionClass, 'isTrait')) {
                                if ($reflectionClass->isTrait()) {
                                    throw new CompilerException('Traits cannot be instantiated', $expression);
                                }
                            }
                        }

                        $classEntry = $compilationContext->classDefinition->getClassEntryByClassName($className, $compilationContext, true);
                        if (!$classEntry) {
                            $classNameToFetch = 'SL("'.escape_class($className).'")';
                            $zendClassEntry = $compilationContext->cacheManager->getClassEntryCache()->get($classNameToFetch, false, $compilationContext);
                            $classEntry = $zendClassEntry->getName();
                        } else {
                            $symbolVariable->setAssociatedClass($reflectionClass);
                        }
                    }

                    $symbolVariable->setClassTypes($className);
                }
                $compilationContext->backend->initObject($symbolVariable, $classEntry, $compilationContext);
            }
        }

        /*
         * Mark variable initialized
         */
        $symbolVariable->setIsInitialized(true, $compilationContext);

        /*
         * Don't check the constructor for stdclass instances
         */
        if ($isStdClass) {
            return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression);
        }

        /**
         * Call the constructor
         * For classes in the same extension we check if the class does implement a constructor
         * For external classes we always assume the class does implement a constructor.
         */
        $callConstructor = false;
        if ($compilationContext->compiler->isClass($className)) {
            $classDefinition = $compilationContext->compiler->getClassDefinition($className);

            if ('class' != $classDefinition->getType()) {
                throw new CompilerException('Only classes can be instantiated', $expression);
            }

            $callConstructor = $classDefinition->hasMethod('__construct');
        } else {
            if ($compilationContext->compiler->isBundledClass($className)) {
                $classDefinition = $compilationContext->compiler->getInternalClassDefinition($className);
                $callConstructor = $classDefinition->hasMethod('__construct');
            }
        }

        /* @TODO use the MethodBuilder here */
        if (isset($expression['parameters'])) {
            $callExpr = new Expression([
                'variable' => ['type' => 'variable', 'value' => $symbolVariable->getRealName()],
                'name' => '__construct',
                'parameters' => $expression['parameters'],
                'call-type' => MethodCall::CALL_NORMAL,
                'file' => $expression['file'],
                'line' => $expression['line'],
                'char' => $expression['char'],
                'check' => $callConstructor,
            ]);
        } else {
            $callExpr = new Expression([
                'variable' => ['type' => 'variable', 'value' => $symbolVariable->getRealName()],
                'name' => '__construct',
                'call-type' => MethodCall::CALL_NORMAL,
                'file' => $expression['file'],
                'line' => $expression['line'],
                'char' => $expression['char'],
                'check' => $callConstructor,
            ]);
        }

        /*
         * If we are certain that there is a constructor we call it, otherwise we checked it at runtime.
         */
        if ($callConstructor) {
            $methodCall = new MethodCall();
            $callExpr->setExpectReturn(false);
            $methodCall->compile($callExpr, $compilationContext);
        } else {
            $compilationContext->headersManager->add('kernel/fcall');

            /* @todo, generate the code using builders */
            $compilationContext->backend->checkConstructor($symbolVariable, $compilationContext);
            $codePrinter->increaseLevel();

            $methodCall = new MethodCall();
            $callExpr->setExpectReturn(false);
            $methodCall->compile($callExpr, $compilationContext);

            $codePrinter->decreaseLevel();
            $codePrinter->output('}');
        }

        return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression);
    }
806394
protected function _accessDimensionArray($expression, Variable $variableVariable, CompilationContext $compilationContext)
    {
        $arrayAccess = $expression;

        if ('variable' == $variableVariable->getType()) {
            if ($variableVariable->hasAnyDynamicType('unknown')) {
                throw new CompilerException('Cannot use non-initialized variable as an array', $arrayAccess['left']);
            }

            /*
             * Trying to use a non-object dynamic variable as object
             */
            if ($variableVariable->hasDifferentDynamicType(['undefined', 'array', 'null'])) {
                $compilationContext->logger->warning(
                    'Possible attempt to access array-index on a non-array dynamic variable',
                    ['non-array-access', $arrayAccess['left']]
                );
            }
        }

        /**
         * Resolves the symbol that expects the value.
         */
        $readOnly = false;
        $symbolVariable = $this->expectingVariable;

        if ($this->readOnly) {
            if ($this->expecting && $this->expectingVariable) {
                /*
                 * If a variable is assigned once in the method, we try to promote it
                 * to a read only variable
                 */
                if ('return_value' != $symbolVariable->getName()) {
                    $line = $compilationContext->symbolTable->getLastCallLine();
                    if (false === $line || ($line > 0 && $line < $expression['line'])) {
                        $numberMutations = $compilationContext->symbolTable->getExpectedMutations($symbolVariable->getName());
                        if (1 == $numberMutations) {
                            if ($symbolVariable->getNumberMutations() == $numberMutations) {
                                $symbolVariable->setMemoryTracked(false);
                                $readOnly = true;
                            }
                        }
                    }
                }

                /*
                 * Variable is not read-only or it wasn't promoted
                 */
                if (!$readOnly) {
                    if ('return_value' != $symbolVariable->getName()) {
                        $symbolVariable->observeVariant($compilationContext);
                        $this->readOnly = false;
                    } else {
                        $symbolVariable = $compilationContext->symbolTable->getTempNonTrackedUninitializedVariable('variable', $compilationContext, $expression);
                    }
                }
            } else {
                $symbolVariable = $compilationContext->symbolTable->getTempNonTrackedUninitializedVariable('variable', $compilationContext, $expression);
            }
        } else {
            if ($this->expecting && $this->expectingVariable) {
                /*
                 * If a variable is assigned once in the method, we try to promote it
                 * to a read only variable
                 */
                if ('return_value' != $symbolVariable->getName()) {
                    $line = $compilationContext->symbolTable->getLastCallLine();
                    if (false === $line || ($line > 0 && $line < $expression['line'])) {
                        $numberMutations = $compilationContext->symbolTable->getExpectedMutations($symbolVariable->getName());
                        if (1 == $numberMutations) {
                            if ($symbolVariable->getNumberMutations() == $numberMutations) {
                                $symbolVariable->setMemoryTracked(false);
                                $readOnly = true;
                            }
                        }
                    }
                }

                /*
                 * Variable is not read-only or it wasn't promoted
                 */
                if (!$readOnly) {
                    if ('return_value' != $symbolVariable->getName()) {
                        $symbolVariable->observeVariant($compilationContext);
                        $this->readOnly = false;
                    } else {
                        $symbolVariable = $compilationContext->symbolTable->getTempVariableForObserve('variable', $compilationContext, $expression);
                    }
                }
            } else {
                $symbolVariable = $compilationContext->symbolTable->getTempVariableForObserve('variable', $compilationContext, $expression);
            }
        }

        /*
         * Variable that receives property accesses must be polymorphic
         */
        if (!$symbolVariable->isVariable()) {
            throw new CompilerException('Cannot use variable: '.$symbolVariable->getType().' to assign array index', $expression);
        }

        /*
         * At this point, we don't know the type fetched from the index
         */
        $symbolVariable->setDynamicTypes('undefined');

        if ($this->readOnly || $readOnly) {
            if ($this->noisy) {
                $flags = 'PH_NOISY | PH_READONLY';
            } else {
                $flags = 'PH_READONLY';
            }
        } else {
            if ($this->noisy) {
                $flags = 'PH_NOISY';
            } else {
                $flags = '0';
            }
        }

        /**
         * Right part of expression is the index.
         */
        $expr = new Expression($arrayAccess['right']);
        $exprIndex = $expr->compile($compilationContext);
        $compilationContext->headersManager->add('kernel/array');
        if ('variable' == $exprIndex->getType()) {
            $exprIndex = $compilationContext->symbolTable->getVariableForRead($exprIndex->getCode(), $compilationContext, $expression);
        }
        $compilationContext->backend->arrayFetch($symbolVariable, $variableVariable, $exprIndex, $flags, $arrayAccess, $compilationContext);

        return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression);
    }
854941
protected function _callNormal(array $expression, CompilationContext $compilationContext)
    {
        $funcName = strtolower($expression['name']);

        if ('array' == $funcName) {
            throw new CompilerException("Cannot use 'array' as a function call", $expression);
        }

        /**
         * Try to optimize function calls using existing optimizers.
         */
        $compiledExpr = $this->optimize($funcName, $expression, $this, $compilationContext);
        if (\is_object($compiledExpr)) {
            return $compiledExpr;
        }

        $exists = $this->functionExists($funcName, $compilationContext);

        if (!$exists) {
            $compilationContext->logger->warning(
                sprintf('Function "%s" does not exist at compile time', $funcName),
                ['nonexistent-function', $expression]
            );
        }

        /*
         * Static variables can be passed using local variables saving memory if the function is read only
         */
        if ($exists) {
            $readOnly = $this->isReadOnly($funcName, $expression);
        } else {
            $readOnly = false;
        }

        /*
         * Resolve parameters
         */
        if (isset($expression['parameters'])) {
            if ($readOnly) {
                $params = $this->getReadOnlyResolvedParams($expression['parameters'], $compilationContext, $expression);
            } else {
                $params = $this->getResolvedParams($expression['parameters'], $compilationContext, $expression);
            }
        } else {
            $params = [];
        }

        /*
         * Some functions receive parameters as references
         * We mark those parameters temporary as references to properly pass them
         */
        $this->markReferences($funcName, $params, $compilationContext, $references, $expression);
        $codePrinter = $compilationContext->codePrinter;

        /*
         * Process the expected symbol to be returned
         */
        $this->processExpectedObservedReturn($compilationContext);

        /**
         * At this point the function will be called in the PHP userland.
         * PHP functions only return zvals so we need to validate the target variable is also a zval.
         */
        $symbolVariable = $this->getSymbolVariable();
        if ($symbolVariable) {
            if (!$symbolVariable->isVariable()) {
                throw new CompilerException(
                    'Returned values by functions can only be assigned to variant variables',
                    $expression
                );
            }

            /*
             * We don't know the exact dynamic type returned by the method call
             */
            $symbolVariable->setDynamicTypes('undefined');
            $symbol = $compilationContext->backend->getVariableCodePointer($symbolVariable);
        }

        /*
         * Include fcall header
         */
        $compilationContext->headersManager->add('kernel/fcall');

        /*
         * Call functions must grown the stack
         */
        $compilationContext->symbolTable->mustGrownStack(true);

        /**
         * Check if the function can have an inline cache.
         */
        $functionCache = $compilationContext->cacheManager->getFunctionCache();
        $cachePointer = $functionCache->get($funcName, $compilationContext, $this, $exists);

        /*
         * Add the last call status to the current symbol table
         */
        $this->addCallStatusFlag($compilationContext);

        if (!\count($params)) {
            if ($this->isExpectingReturn()) {
                if ('return_value' == $symbolVariable->getName()) {
                    $codePrinter->output(
                        'ZEPHIR_RETURN_CALL_FUNCTION("'.$funcName.'", '.$cachePointer.');'
                    );
                } else {
                    if ($this->mustInitSymbolVariable()) {
                        $symbolVariable->setMustInitNull(true);
                        $symbolVariable->trackVariant($compilationContext);
                    }
                    $codePrinter->output(
                        'ZEPHIR_CALL_FUNCTION('.$symbol.', "'.$funcName.'", '.$cachePointer.');'
                    );
                }
            } else {
                $codePrinter->output('ZEPHIR_CALL_FUNCTION(NULL, "'.$funcName.'", '.$cachePointer.');');
            }
        } else {
            if ($this->isExpectingReturn()) {
                if ('return_value' == $symbolVariable->getName()) {
                    $codePrinter->output(
                        strtr('ZEPHIR_RETURN_CALL_FUNCTION(":func", :pointer, :params);', [
                            ':func' => $funcName,
                            ':pointer' => $cachePointer,
                            ':params' => implode(', ', $params),
                        ])
                    );
                } else {
                    if ($this->mustInitSymbolVariable()) {
                        $symbolVariable->setMustInitNull(true);
                        $symbolVariable->trackVariant($compilationContext);
                    }

                    $codePrinter->output(
                        strtr('ZEPHIR_CALL_FUNCTION(:symbol, ":func", :pointer, :params);', [
                            ':symbol' => $symbol,
                            ':func' => $funcName,
                            ':pointer' => $cachePointer,
                            ':params' => implode(', ', $params),
                        ])
                    );
                }
            } else {
                $codePrinter->output(
                    strtr('ZEPHIR_CALL_FUNCTION(NULL, ":func", :pointer, :params);', [
                        ':func' => $funcName,
                        ':pointer' => $cachePointer,
                        ':params' => implode(', ', $params),
                    ])
                );
            }
        }

        /*
         * Temporary variables must be copied if they have more than one reference
         */
        foreach ($this->getMustCheckForCopyVariables() as $checkVariable) {
            $codePrinter->output('zephir_check_temp_parameter('.$checkVariable.');');
        }

        if (\is_array($references)) {
            foreach ($references as $reference) {
                $variable = $compilationContext->symbolTable->getVariable($reference, $compilationContext);
                $compilationContext->codePrinter->output(
                    'ZEPHIR_UNREF('.$compilationContext->backend->getVariableCode($variable).');'
                );
            }
        }

        $this->addCallStatusOrJump($compilationContext);

        /*
         * We can mark temporary variables generated as idle
         */
        foreach ($this->getTemporalVariables() as $tempVariable) {
            $tempVariable->setIdle(true);
        }

        if ($this->isExpectingReturn()) {
            return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression);
        }

        return new CompiledExpression('null', null, $expression);
    }