Пример #1
0
 /**
  * Resolves an expression
  *
  * @param CompilationContext $compilationContext
  * @return bool|CompiledExpression|mixed
  * @throws CompilerException
  */
 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':
             if (!$this->_stringOperation) {
                 if (ctype_digit($expression['value'])) {
                     return new CompiledExpression('int', $expression['value'], $expression);
                 }
             }
             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 ($var->getRealName() == 'this') {
                     $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 ($expression['left']['type'] == 'list') {
                 $compilationContext->logger->warning("Unnecessary extra parentheses", "extra-parentheses", $expression);
             }
             $numberPrints = $compilationContext->codePrinter->getNumberPrints();
             $expr = new Expression($expression['left']);
             $expr->setExpectReturn($this->_expecting, $this->_expectingVariable);
             $resolved = $expr->compile($compilationContext);
             if ($compilationContext->codePrinter->getNumberPrints() - $numberPrints <= 1) {
                 if (strpos($resolved->getCode(), ' ') !== false) {
                     return new CompiledExpression($resolved->getType(), '(' . $resolved->getCode() . ')', $expression);
                 }
             }
             return $resolved;
         case 'cast':
             $compilableExpression = new CastOperator();
             break;
         case 'type-hint':
             return $this->compileTypeHint($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);
 }
Пример #2
0
 /**
  * @param CompilationContext $compilationContext
  * @param boolean $unreachable
  * @param int $branchType
  * @return Branch
  */
 public function compile(CompilationContext $compilationContext, $unreachable = false, $branchType = Branch::TYPE_UNKNOWN)
 {
     $compilationContext->codePrinter->increaseLevel();
     $compilationContext->currentBranch++;
     /**
      * Create a new branch
      */
     $currentBranch = new Branch();
     $currentBranch->setType($branchType);
     $currentBranch->setUnreachable($unreachable);
     /**
      * Activate branch in the branch manager
      */
     $compilationContext->branchManager->addBranch($currentBranch);
     $this->_unreachable = $unreachable;
     $statements = $this->_statements;
     foreach ($statements as $statement) {
         /**
          * Generate GDB hints
          */
         if ($this->_debug) {
             if (isset($statement['file'])) {
                 if ($statement['type'] != 'declare' && $statement['type'] != 'comment') {
                     $compilationContext->codePrinter->outputNoIndent('#line ' . $statement['line'] . ' "' . $statement['file'] . '"');
                 }
             }
         }
         /**
          * Show warnings if code is generated when the 'unreachable state' is 'on'
          */
         if ($this->_unreachable === true) {
             switch ($statement['type']) {
                 case 'echo':
                     $compilationContext->logger->warning('Unreachable code', "unreachable-code", $statement['expressions'][0]);
                     break;
                 case 'let':
                     $compilationContext->logger->warning('Unreachable code', "unreachable-code", $statement['assignments'][0]);
                     break;
                 case 'fetch':
                 case 'fcall':
                 case 'mcall':
                 case 'scall':
                 case 'if':
                 case 'while':
                 case 'do-while':
                 case 'switch':
                 case 'for':
                 case 'return':
                 case 'c-block':
                     if (isset($statement['expr'])) {
                         $compilationContext->logger->warning('Unreachable code', "unreachable-code", $statement['expr']);
                     } else {
                         $compilationContext->logger->warning('Unreachable code', "unreachable-code", $statement);
                     }
                     break;
                 default:
                     $compilationContext->logger->warning('Unreachable code', "unreachable-code", $statement);
             }
         }
         switch ($statement['type']) {
             case 'let':
                 $letStatement = new LetStatement($statement);
                 $letStatement->compile($compilationContext);
                 break;
             case 'echo':
                 $echoStatement = new EchoStatement($statement);
                 $echoStatement->compile($compilationContext);
                 break;
             case 'declare':
                 $declareStatement = new DeclareStatement($statement);
                 $declareStatement->compile($compilationContext);
                 break;
             case 'if':
                 $ifStatement = new IfStatement($statement);
                 $ifStatement->compile($compilationContext);
                 break;
             case 'while':
                 $whileStatement = new WhileStatement($statement);
                 $whileStatement->compile($compilationContext);
                 break;
             case 'do-while':
                 $doWhileStatement = new DoWhileStatement($statement);
                 $doWhileStatement->compile($compilationContext);
                 break;
             case 'switch':
                 $switchStatement = new SwitchStatement($statement);
                 $switchStatement->compile($compilationContext);
                 break;
             case 'for':
                 $forStatement = new ForStatement($statement);
                 $forStatement->compile($compilationContext);
                 break;
             case 'return':
                 $returnStatement = new ReturnStatement($statement);
                 $returnStatement->compile($compilationContext);
                 $this->_unreachable = true;
                 break;
             case 'require':
                 $requireStatement = new RequireStatement($statement);
                 $requireStatement->compile($compilationContext);
                 break;
             case 'loop':
                 $loopStatement = new LoopStatement($statement);
                 $loopStatement->compile($compilationContext);
                 break;
             case 'break':
                 $breakStatement = new BreakStatement($statement);
                 $breakStatement->compile($compilationContext);
                 $this->_unreachable = true;
                 break;
             case 'continue':
                 $continueStatement = new ContinueStatement($statement);
                 $continueStatement->compile($compilationContext);
                 $this->_unreachable = true;
                 break;
             case 'unset':
                 $unsetStatement = new UnsetStatement($statement);
                 $unsetStatement->compile($compilationContext);
                 break;
             case 'throw':
                 $throwStatement = new ThrowStatement($statement);
                 $throwStatement->compile($compilationContext);
                 $this->_unreachable = true;
                 break;
             case 'try-catch':
                 $throwStatement = new TryCatchStatement($statement);
                 $throwStatement->compile($compilationContext);
                 $this->_unreachable = false;
                 break;
             case 'fetch':
                 $expr = new Expression($statement['expr']);
                 $expr->setExpectReturn(false);
                 $compiledExpression = $expr->compile($compilationContext);
                 $compilationContext->codePrinter->output($compiledExpression->getCode() . ';');
                 break;
             case 'mcall':
                 $methodCall = new MethodCall();
                 $expr = new Expression($statement['expr']);
                 $expr->setExpectReturn(false);
                 $methodCall->compile($expr, $compilationContext);
                 break;
             case 'fcall':
                 $functionCall = new FunctionCall();
                 $expr = new Expression($statement['expr']);
                 $expr->setExpectReturn(false);
                 $compiledExpression = $functionCall->compile($expr, $compilationContext);
                 switch ($compiledExpression->getType()) {
                     case 'int':
                     case 'double':
                     case 'uint':
                     case 'long':
                     case 'ulong':
                     case 'char':
                     case 'uchar':
                     case 'bool':
                         $compilationContext->codePrinter->output($compiledExpression->getCode() . ';');
                         break;
                 }
                 break;
             case 'scall':
                 $methodCall = new StaticCall();
                 $expr = new Expression($statement['expr']);
                 $expr->setExpectReturn(false);
                 $methodCall->compile($expr, $compilationContext);
                 break;
             case 'cblock':
                 $compilationContext->codePrinter->output($statement['value']);
                 break;
             case 'empty':
                 break;
             default:
                 throw new Exception('Unsupported statement: ' . $statement['type']);
         }
         if ($statement['type'] != 'comment') {
             $this->_lastStatement = $statement;
         }
     }
     /**
      * Traverses temporal variables created in a specific branch
      * marking them as idle
      */
     $compilationContext->symbolTable->markTemporalVariablesIdle($compilationContext);
     $compilationContext->branchManager->removeBranch($currentBranch);
     $compilationContext->currentBranch--;
     $compilationContext->codePrinter->decreaseLevel();
     return $currentBranch;
 }
Пример #3
0
 /**
  * Compiles the method
  *
  * @param CompilationContext $compilationContext
  * @return null
  * @throws CompilerException
  */
 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 passses
      */
     $localContext = $this->localContext;
     $typeInference = $this->typeInference;
     $callGathererPass = $this->callGathererPass;
     /**
      * Every method has its own symbol table
      */
     $symbolTable = new SymbolTable($compilationContext);
     if ($localContext) {
         $symbolTable->setLocalContext($localContext);
     }
     /**
      * Parameters has an additional extra mutation
      */
     $parameters = $this->parameters;
     if ($localContext) {
         if (is_object($parameters)) {
             foreach ($parameters->getParameters() as $parameter) {
                 $localContext->increaseMutations($parameter['name']);
             }
         }
     }
     /**
      * 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);
     /**
      * Cache Manager manages function calls, method calls and class entries caches
      */
     $cacheManager = new CacheManager();
     $cacheManager->setGatherer($callGathererPass);
     $compilationContext->branchManager = $branchManager;
     $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 = array();
         foreach ($parameters->getParameters() as $parameter) {
             /**
              * Change dynamic variables to low level types
              */
             if ($typeInference) {
                 if (isset($parameter['data-type'])) {
                     if ($parameter['data-type'] == 'variable') {
                         $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);
                         break;
                     default:
                         $symbol = $symbolTable->addVariable($parameter['data-type'], $parameter['name'], $compilationContext);
                         $symbolParam = $symbolTable->addVariable('variable', $parameter['name'] . '_param', $compilationContext);
                         if ($parameter['data-type'] == 'string' || $parameter['data-type'] == 'array') {
                             $symbol->setMustInitNull(true);
                         }
                         break;
                 }
             } else {
                 $symbol = $symbolTable->addVariable('variable', $parameter['name'], $compilationContext);
             }
             /**
              * 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, $parameter);
                 /**
                  * Initialize auxiliar parameter zvals to null
                  */
                 $symbolParam->setMustInitNull(true);
                 /**
                  * Increase uses
                  */
                 $symbolParam->increaseUses();
             } else {
                 if (isset($parameter['default'])) {
                     if (isset($parameter['data-type'])) {
                         if ($parameter['data-type'] == 'variable') {
                             $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, $parameter);
             /**
              * 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[] = array($symbol, $parameter);
             } else {
                 if (isset($parameter['data-type'])) {
                     if ($parameter['data-type'] == 'variable') {
                         $symbol->setDynamicTypes('undefined');
                     }
                 } else {
                     $symbol->setDynamicTypes('undefined');
                 }
             }
         }
     }
     /**
      * Set properties of the class, if constructor
      */
     $classDefinition = $this->classDefinition;
     if ($this->isConstructor() || $this->getName() == 'unserialize' && in_array('Serializable', $classDefinition->getImplementedInterfaces())) {
         $initMethod = $classDefinition->getMethod('zephir_init_properties');
         if ($initMethod && $initMethod->getClassDefinition() == $classDefinition) {
             $codePrinter->increaseLevel();
             /* Only initialize properties if the constructor was called directly on the class, e.g. make parent:: calls not trigger property initialization */
             if ($this->isConstructor()) {
                 $codePrinter->output('if (EG(called_scope) == ' . $classDefinition->getClassEntry() . ') {');
                 $codePrinter->increaseLevel();
             }
             $codePrinter->output('zephir_init_properties(this_ptr TSRMLS_CC);');
             if ($this->isConstructor()) {
                 $codePrinter->decreaseLevel();
                 $codePrinter->output('}');
             }
             $codePrinter->decreaseLevel();
         }
         $extendsClass = $classDefinition->getExtendsClassDefinition();
         $parentConstructor = $extendsClass ? $extendsClass->getMethod('__construct') : null;
         if ($this->isConstructor() && $extendsClass && $parentConstructor && !$extendsClass->isGeneratedConstructor() && $classDefinition->isGeneratedConstructor()) {
             /**
              * Call the parent constructor (create parent::__construct call)
              * since no constructor was actually defined for this class, but
              * generated for zephir to ensure property initialization
              */
             $callExpr = new Expression(array('type' => 'scall', 'dynamic-class' => 0, 'class' => 'parent', 'dynamic' => 0, 'name' => '__construct'));
             $call = new StaticCall();
             $expr = $callExpr;
             $expr->setExpectReturn(false);
             $call->compile($expr, $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 default values in dynamic variables
      */
     $initVarCode = "";
     foreach ($symbolTable->getVariables() as $variable) {
         /**
          * Initialize 'dynamic' variables with default values
          */
         if ($variable->getType() == 'variable') {
             if ($variable->getNumberUses() > 0) {
                 if ($variable->getName() != 'this_ptr' && $variable->getName() != 'return_value' && $variable->getName() != 'return_value_ptr') {
                     $defaultValue = $variable->getDefaultInitValue();
                     if (is_array($defaultValue)) {
                         $symbolTable->mustGrownStack(true);
                         switch ($defaultValue['type']) {
                             case 'int':
                             case 'uint':
                             case 'long':
                                 $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                                 $initVarCode .= "\t" . 'ZVAL_LONG(' . $variable->getName() . ', ' . $defaultValue['value'] . ');' . PHP_EOL;
                                 break;
                             case 'char':
                             case 'uchar':
                                 if (strlen($defaultValue['value']) > 2) {
                                     if (strlen($defaultValue['value']) > 10) {
                                         throw new CompilerException("Invalid char literal: '" . substr($defaultValue['value'], 0, 10) . "...'", $defaultValue);
                                     } else {
                                         throw new CompilerException("Invalid char literal: '" . $defaultValue['value'] . "'", $defaultValue);
                                     }
                                 }
                                 $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                                 $initVarCode .= "\t" . 'ZVAL_LONG(' . $variable->getName() . ', \'' . $defaultValue['value'] . '\');' . PHP_EOL;
                                 break;
                             case 'null':
                                 $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                                 $initVarCode .= "\t" . 'ZVAL_NULL(' . $variable->getName() . ');' . PHP_EOL;
                                 break;
                             case 'double':
                                 $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                                 $initVarCode .= "\t" . 'ZVAL_DOUBLE(' . $variable->getName() . ', ' . $defaultValue['value'] . ');' . PHP_EOL;
                                 break;
                             case 'string':
                                 $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                                 $initVarCode .= "\t" . 'ZVAL_STRING(' . $variable->getName() . ', "' . Utils::addSlashes($defaultValue['value'], true) . '", 1);' . PHP_EOL;
                                 break;
                             case 'array':
                             case 'empty-array':
                                 $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                                 $initVarCode .= "\t" . 'array_init(' . $variable->getName() . ');' . PHP_EOL;
                                 break;
                             default:
                                 throw new CompilerException('Invalid default type: ' . $defaultValue['type'] . ' for data type: ' . $variable->getType(), $variable->getOriginal());
                         }
                     }
                 }
             }
             continue;
         }
         /**
          * Initialize 'string' variables with default values
          */
         if ($variable->getType() == 'string') {
             if ($variable->getNumberUses() > 0) {
                 $defaultValue = $variable->getDefaultInitValue();
                 if (is_array($defaultValue)) {
                     $symbolTable->mustGrownStack(true);
                     switch ($defaultValue['type']) {
                         case 'string':
                             $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                             $initVarCode .= "\t" . 'ZVAL_STRING(' . $variable->getName() . ', "' . Utils::addSlashes($defaultValue['value'], true) . '", 1);' . PHP_EOL;
                             break;
                         case 'null':
                             $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                             $initVarCode .= "\t" . 'ZVAL_EMPTY_STRING(' . $variable->getName() . ');' . PHP_EOL;
                             break;
                         default:
                             throw new CompilerException('Invalid default type: ' . $defaultValue['type'] . ' for data type: ' . $variable->getType(), $variable->getOriginal());
                     }
                 }
             }
             continue;
         }
         /**
          * Initialize 'array' variables with default values
          */
         if ($variable->getType() == 'array') {
             if ($variable->getNumberUses() > 0) {
                 $defaultValue = $variable->getDefaultInitValue();
                 if (is_array($defaultValue)) {
                     $symbolTable->mustGrownStack(true);
                     switch ($defaultValue['type']) {
                         case 'null':
                             $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                             $initVarCode .= "\t" . 'ZVAL_NULL(' . $variable->getName() . ');' . PHP_EOL;
                             break;
                         case 'array':
                         case 'empty-array':
                             $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                             $initVarCode .= "\t" . 'array_init(' . $variable->getName() . ');' . PHP_EOL;
                             break;
                         default:
                             throw new CompilerException('Invalid default type: ' . $defaultValue['type'] . ' for data type: ' . $variable->getType(), $variable->getOriginal());
                     }
                 }
             }
         }
     }
     /**
      * Fetch parameters from vm-top
      */
     $initCode = "";
     $code = "";
     if (is_object($parameters)) {
         /**
          * Round 2. Fetch the parameters in the method
          */
         $params = array();
         $requiredParams = array();
         $optionalParams = array();
         $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 = array();
         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 ($dataType != 'variable') {
                 /**
                  * 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
              */
             $initCode .= "\t" . 'if (!' . $name . ') {' . PHP_EOL;
             $initCode .= $this->assignDefaultValue($parameter, $compilationContext);
             if (isset($parametersToSeparate[$name]) || $dataType != 'variable') {
                 $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 . ', ' . join(', ', $params) . ');' . PHP_EOL;
             } else {
                 $code .= "\t" . 'zephir_fetch_params(0, ' . $numberRequiredParams . ', ' . $numberOptionalParams . ', ' . join(', ', $params) . ');' . PHP_EOL;
             }
         } else {
             foreach ($params as $param) {
                 $code .= "\t" . $param . ' = ' . $param . '_ext;' . PHP_EOL;
             }
         }
         $code .= PHP_EOL;
     }
     $code .= $initCode . $initVarCode;
     $codePrinter->preOutput($code);
     /**
      * Fetch used superglobals
      */
     foreach ($symbolTable->getVariables() as $name => $variable) {
         if ($symbolTable->isSuperGlobal($name)) {
             $codePrinter->preOutput("\t" . 'zephir_get_global(&' . $name . ', SS("' . $name . '") TSRMLS_CC);');
         }
     }
     /**
      * 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 = array();
     $completeName = $compilationContext->classDefinition->getCompleteName();
     foreach ($symbolTable->getVariables() as $variable) {
         if ($variable->getNumberUses() <= 0) {
             if ($variable->isExternal() == false) {
                 $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 ($variable->getName() != 'this_ptr' && $variable->getName() != 'return_value' && $variable->getName() != 'return_value_ptr') {
             $type = $variable->getType();
             if (!isset($usedVariables[$type])) {
                 $usedVariables[$type] = array();
             }
             $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 ($variable->isExternal() == true || $variable->isTemporal()) {
             continue;
         }
         if ($variable->getName() == 'this_ptr' || $variable->getName() == 'return_value' || $variable->getName() == 'return_value_ptr' || $variable->getName() == 'ZEPHIR_LAST_CALL_STATUS') {
             continue;
         }
         if (!$variable->isUsed()) {
             $node = $variable->getLastUsedNode();
             if (is_array($node)) {
                 $compilationContext->logger->warning('Variable "' . $variable->getName() . '" assigned but not used in ' . $completeName . '::' . $this->getName(), "unused-variable", $node);
             } 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
      */
     foreach ($usedVariables as $type => $variables) {
         $pointer = null;
         switch ($type) {
             case 'int':
                 $code = 'int ';
                 break;
             case 'uint':
                 $code = 'unsigned int ';
                 break;
             case 'char':
                 $code = 'char ';
                 break;
             case 'uchar':
                 $code = 'unsigned char ';
                 break;
             case 'long':
                 $code = 'long ';
                 break;
             case 'ulong':
                 $code = 'unsigned long ';
                 break;
             case 'bool':
                 $code = 'zend_bool ';
                 break;
             case 'double':
                 $code = 'double ';
                 break;
             case 'string':
             case 'variable':
             case 'array':
             case 'null':
                 $pointer = '*';
                 $code = 'zval ';
                 break;
             case 'HashTable':
                 $pointer = '*';
                 $code = 'HashTable ';
                 break;
             case 'HashPosition':
                 $code = 'HashPosition ';
                 break;
             case 'zend_class_entry':
                 $pointer = '*';
                 $code = 'zend_class_entry ';
                 break;
             case 'zend_function':
                 $pointer = '*';
                 $code = 'zend_function ';
                 break;
             case 'zend_object_iterator':
                 $pointer = '*';
                 $code = 'zend_object_iterator ';
                 break;
             case 'zend_property_info':
                 $pointer = '*';
                 $code = 'zend_property_info ';
                 break;
             case 'zephir_fcall_cache_entry':
                 $pointer = '*';
                 $code = 'zephir_fcall_cache_entry ';
                 break;
             case 'static_zephir_fcall_cache_entry':
                 $pointer = '*';
                 $code = 'zephir_nts_static zephir_fcall_cache_entry ';
                 break;
             case 'static_zend_class_entry':
                 $pointer = '*';
                 $code = 'zephir_nts_static zend_class_entry ';
                 break;
             case 'zephir_ce_guard':
                 $code = 'zephir_nts_static zend_bool ';
                 break;
             default:
                 throw new CompilerException("Unsupported type in declare: " . $type);
         }
         $groupVariables = array();
         $defaultValues = array();
         /**
          * @var $variables Variable[]
          */
         foreach ($variables as $variable) {
             $isComplex = $type == 'variable' || $type == 'string' || $type == 'array' || $type == 'resource' || $type == 'callable' || $type == 'object';
             if ($isComplex && $variable->mustInitNull()) {
                 if ($variable->isLocalOnly()) {
                     $groupVariables[] = $variable->getName() . ' = zval_used_for_init';
                 } else {
                     if ($variable->isDoublePointer()) {
                         $groupVariables[] = $pointer . $pointer . $variable->getName() . ' = NULL';
                     } else {
                         $groupVariables[] = $pointer . $variable->getName() . ' = NULL';
                     }
                 }
                 continue;
             }
             if ($variable->isLocalOnly()) {
                 $groupVariables[] = $variable->getName();
                 continue;
             }
             if ($variable->isDoublePointer()) {
                 if ($variable->mustInitNull()) {
                     $groupVariables[] = $pointer . $pointer . $variable->getName() . ' = NULL';
                 } else {
                     $groupVariables[] = $pointer . $pointer . $variable->getName();
                 }
                 continue;
             }
             $defaultValue = $variable->getDefaultInitValue();
             if ($defaultValue !== null) {
                 switch ($type) {
                     case 'variable':
                     case 'string':
                     case 'array':
                     case 'resource':
                     case 'callable':
                     case 'object':
                         $groupVariables[] = $pointer . $variable->getName();
                         break;
                     case 'char':
                         if (strlen($defaultValue) > 4) {
                             if (strlen($defaultValue) > 10) {
                                 throw new CompilerException("Invalid char literal: '" . substr($defaultValue, 0, 10) . "...'", $variable->getOriginal());
                             } else {
                                 throw new CompilerException("Invalid char literal: '" . $defaultValue . "'", $variable->getOriginal());
                             }
                         }
                         /* no break */
                     /* no break */
                     default:
                         $groupVariables[] = $pointer . $variable->getName() . ' = ' . $defaultValue;
                         break;
                 }
                 continue;
             }
             if ($variable->mustInitNull() && $pointer) {
                 $groupVariables[] = $pointer . $variable->getName() . ' = NULL';
                 continue;
             }
             $groupVariables[] = $pointer . $variable->getName();
         }
         $codePrinter->preOutput("\t" . $code . join(', ', $groupVariables) . ';');
     }
     /**
      * 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 ($lastType != 'return' && $lastType != 'throw' && !$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']);
             }
         }
     }
     /**
      * 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;
 }