Ejemplo n.º 1
0
 /**
  * Processes the symbol variable that will be used to return
  * the result of the symbol call. If a temporal variable is used
  * as returned value only the body is freed between calls
  *
  * @param CompilationContext $compilationContext
  */
 public function processExpectedComplexLiteralReturn(CompilationContext $compilationContext)
 {
     $expr = $this->_expression;
     $expression = $expr->getExpression();
     /**
      * Create temporary variable if needed
      */
     $mustInit = false;
     $isExpecting = $expr->isExpectingReturn();
     if ($isExpecting) {
         $symbolVariable = $expr->getExpectingVariable();
         if (is_object($symbolVariable)) {
             $readDetector = new ReadDetector($expression);
             if ($readDetector->detect($symbolVariable->getName(), $expression)) {
                 $symbolVariable = $compilationContext->symbolTable->getTempComplexLiteralVariableForWrite('variable', $compilationContext, $expression);
             } else {
                 $mustInit = true;
             }
         } else {
             $symbolVariable = $compilationContext->symbolTable->getTempComplexLiteralVariableForWrite('variable', $compilationContext, $expression);
         }
     }
     $this->_mustInit = $mustInit;
     $this->_symbolVariable = $symbolVariable;
     $this->_isExpecting = $isExpecting;
 }
Ejemplo n.º 2
0
 /**
  * Compiles a method call
  *
  * @param Expression $expr
  * @param CompilationContext $compilationContext
  */
 public function compile(Expression $expr, CompilationContext $compilationContext)
 {
     $expression = $expr->getExpression();
     $exprVariable = new Expression($expression['variable']);
     $exprVariable->setReadOnly(true);
     $exprCompiledVariable = $exprVariable->compile($compilationContext);
     $builtInType = false;
     switch ($exprCompiledVariable->getType()) {
         case 'variable':
             $variableVariable = $compilationContext->symbolTable->getVariableForRead($exprCompiledVariable->getCode(), $compilationContext, $expression);
             switch ($variableVariable->getType()) {
                 case 'variable':
                     $caller = $variableVariable;
                     break;
                 default:
                     /* Check if there is a built-in type optimizer available */
                     $builtInTypeClass = 'Zephir\\Types\\' . ucfirst($variableVariable->getType()) . 'Type';
                     if (class_exists($builtInTypeClass)) {
                         /**
                          * @var $builtInType \Zephir\Types\AbstractType
                          */
                         $builtInType = new $builtInTypeClass();
                         $caller = $exprCompiledVariable;
                     } else {
                         throw new CompilerException("Methods cannot be called on variable type: " . $variableVariable->getType(), $expression);
                     }
             }
             break;
         default:
             /* Check if there is a built-in type optimizer available */
             $builtInTypeClass = 'Zephir\\Types\\' . ucfirst($exprCompiledVariable->getType()) . 'Type';
             if (class_exists($builtInTypeClass)) {
                 $builtInType = new $builtInTypeClass();
                 $caller = $exprCompiledVariable;
             } else {
                 throw new CompilerException("Cannot use expression: " . $exprCompiledVariable->getType() . " as method caller", $expression['variable']);
             }
     }
     $codePrinter = $compilationContext->codePrinter;
     $type = $expression['call-type'];
     /**
      * In normal method calls and dynamic string method calls we just use the name given by the user
      */
     if ($type == self::CALL_NORMAL || $type == self::CALL_DYNAMIC_STRING) {
         $methodName = strtolower($expression['name']);
     } else {
         $variableMethod = $compilationContext->symbolTable->getVariableForRead($expression['name'], $compilationContext, $expression);
         if (is_object($builtInType)) {
             throw new CompilerException("Dynamic method invocation for type: " . $variableMethod->getType() . " is not supported", $expression);
         }
         if ($variableMethod->isNotVariableAndString()) {
             throw new CompilerException("Cannot use variable type: " . $variableMethod->getType() . " as dynamic method name", $expression);
         }
     }
     $symbolVariable = null;
     /**
      * Create temporary variable if needed
      */
     $mustInit = false;
     $isExpecting = $expr->isExpectingReturn();
     if ($isExpecting) {
         $symbolVariable = $expr->getExpectingVariable();
         if (is_object($symbolVariable)) {
             $readDetector = new ReadDetector($expression);
             if ($caller == $symbolVariable || $readDetector->detect($symbolVariable->getName(), $expression)) {
                 $symbolVariable = $compilationContext->symbolTable->getTempVariableForObserveOrNullify('variable', $compilationContext, $expression);
             } else {
                 $mustInit = true;
             }
         } else {
             $symbolVariable = $compilationContext->symbolTable->getTempVariableForObserveOrNullify('variable', $compilationContext, $expression);
         }
     }
     /**
      * Method calls only return zvals so we need to validate the target variable is also a zval
      */
     if (!$builtInType) {
         if ($isExpecting) {
             if (!$symbolVariable->isVariable()) {
                 throw new CompilerException("Returned values by functions can only be assigned to variant variables", $expression);
             }
             /**
              * At this point, we don't know the exact dynamic type returned by the method call
              */
             $symbolVariable->setDynamicTypes('undefined');
         }
     } else {
         return $builtInType->invokeMethod($methodName, $caller, $compilationContext, $this, $expression);
     }
     $check = true;
     if (isset($expression['check'])) {
         $check = $expression['check'];
     }
     /**
      * Try to check if the method exist in the callee, only when method call is self::CALL_NORMAL
      */
     if ($type == self::CALL_NORMAL) {
         if ($variableVariable->getRealName() == 'this') {
             $classDefinition = $compilationContext->classDefinition;
             if (!$classDefinition->hasMethod($methodName)) {
                 if ($check) {
                     $found = false;
                     $interfaces = $classDefinition->isAbstract() ? $classDefinition->getImplementedInterfaces() : null;
                     if (is_array($interfaces)) {
                         $compiler = $compilationContext->compiler;
                         foreach ($interfaces as $interface) {
                             $classInterfaceDefinition = $compiler->getClassDefinition($interface);
                             if ($classInterfaceDefinition->hasMethod($methodName)) {
                                 $found = true;
                                 $classMethod = $classInterfaceDefinition->getMethod($methodName);
                                 break;
                             }
                         }
                     }
                     if (!$found) {
                         $possibleMethod = $classDefinition->getPossibleMethodName($expression['name']);
                         if ($possibleMethod && $expression['name'] != $possibleMethod) {
                             throw new CompilerException("Class '" . $classDefinition->getCompleteName() . "' does not implement method: '" . $expression['name'] . "'. Did you mean '" . $possibleMethod . "'?", $expression);
                         } else {
                             throw new CompilerException("Class '" . $classDefinition->getCompleteName() . "' does not implement method: '" . $expression['name'] . "'", $expression);
                         }
                     }
                 }
             } else {
                 if ($check) {
                     $classMethod = $classDefinition->getMethod($methodName);
                 }
             }
             if ($check) {
                 /**
                  * Private methods must be called in their declaration scope
                  */
                 if ($classMethod->isPrivate()) {
                     if ($classMethod->getClassDefinition() != $classDefinition) {
                         throw new CompilerException("Cannot call private method '" . $expression['name'] . "' out of its scope", $expression);
                     }
                 }
                 /**
                  * Try to produce an exception if method is called with a wrong number of parameters
                  */
                 if (isset($expression['parameters'])) {
                     $callNumberParameters = count($expression['parameters']);
                 } else {
                     $callNumberParameters = 0;
                 }
                 $expectedNumberParameters = $classMethod->getNumberOfRequiredParameters();
                 if (!$expectedNumberParameters && $callNumberParameters > 0) {
                     $numberParameters = $classMethod->getNumberOfParameters();
                     if ($callNumberParameters > $numberParameters) {
                         throw new CompilerException("Method '" . $classDefinition->getCompleteName() . "::" . $expression['name'] . "' called with a wrong number of parameters, the method has: " . $expectedNumberParameters . ", passed: " . $callNumberParameters, $expression);
                     }
                 }
                 if ($callNumberParameters < $expectedNumberParameters) {
                     throw new CompilerException("Method '" . $classDefinition->getCompleteName() . "::" . $expression['name'] . "' called with a wrong number of parameters, the method has: " . $expectedNumberParameters . ", passed: " . $callNumberParameters, $expression);
                 }
                 $method = $classMethod;
             }
         } else {
             /**
              * Variables whose dynamic type is 'object' can be used
              * to determine method existence in compile time
              */
             if ($check && $variableVariable->hasAnyDynamicType('object')) {
                 $classTypes = $variableVariable->getClassTypes();
                 if (count($classTypes)) {
                     $numberImplemented = 0;
                     $compiler = $compilationContext->compiler;
                     foreach ($classTypes as $classType) {
                         if ($compiler->isClass($classType) || $compiler->isInterface($classType) || $compiler->isBundledClass($classType) || $compiler->isBundledInterface($classType)) {
                             if ($compiler->isClass($classType) || $compiler->isInterface($classType)) {
                                 $classDefinition = $compiler->getClassDefinition($classType);
                             } else {
                                 $classDefinition = $compiler->getInternalClassDefinition($classType);
                             }
                             if (!$classDefinition) {
                                 throw new CompilerException("Cannot locate class definition for class " . $classType, $expression);
                             }
                             if (!$classDefinition->hasMethod($methodName)) {
                                 if (!$classDefinition->isInterface()) {
                                     if (count($classTypes) == 1) {
                                         throw new CompilerException("Class '" . $classType . "' does not implement method: '" . $expression['name'] . "'", $expression);
                                     }
                                 }
                                 continue;
                             }
                             $method = $classDefinition->getMethod($methodName);
                             /**
                              * Private methods must be called in their declaration scope
                              */
                             if ($method->isPrivate()) {
                                 if ($method->getClassDefinition() != $classDefinition) {
                                     throw new CompilerException("Cannot call private method '" . $expression['name'] . "' out of its scope", $expression);
                                 }
                             }
                             /**
                              * Check visibility for protected methods
                              */
                             if ($method->isProtected() && $method->getClassDefinition() != $classDefinition && $method->getClassDefinition() != $classDefinition->getExtendsClass()) {
                                 throw new CompilerException("Cannot call protected method '" . $expression['name'] . "' out of its scope", $expression);
                             }
                             /**
                              * Try to produce an exception if a method is called with a wrong number of parameters
                              * We only check extension parameters if methods are extension methods
                              * Internal methods may have invalid Reflection information
                              */
                             if ($method instanceof ClassMethod && !$method->isBundled()) {
                                 if (isset($expression['parameters'])) {
                                     $callNumberParameters = count($expression['parameters']);
                                 } else {
                                     $callNumberParameters = 0;
                                 }
                                 $classMethod = $classDefinition->getMethod($methodName);
                                 $expectedNumberParameters = $classMethod->getNumberOfRequiredParameters();
                                 if (!$expectedNumberParameters && $callNumberParameters > 0) {
                                     $numberParameters = $classMethod->getNumberOfParameters();
                                     if ($callNumberParameters > $numberParameters) {
                                         $className = $classDefinition->getCompleteName();
                                         throw new CompilerException("Method '" . $className . "::" . $expression['name'] . "' called with a wrong number of parameters, the method has: " . $expectedNumberParameters . ", passed: " . $callNumberParameters, $expression);
                                     }
                                 }
                                 if ($callNumberParameters < $expectedNumberParameters) {
                                     throw new CompilerException("Method '" . $classDefinition->getCompleteName() . "::" . $expression['name'] . "' called with a wrong number of parameters, the method has: " . $expectedNumberParameters . ", passed: " . $callNumberParameters, $expression);
                                 }
                             }
                             /**
                              * The method is checked in the first class that implements the method
                              * We could probably have collisions here
                              */
                             $numberImplemented++;
                             break;
                         } else {
                             $numberImplemented++;
                             $compilationContext->logger->warning("Class \"" . $classType . "\" does not exist at compile time", "nonexistent-class", $expression);
                         }
                     }
                     if ($numberImplemented == 0) {
                         if (!$classDefinition->isInterface()) {
                             if (count($classTypes) > 1) {
                                 throw new CompilerException("None of classes: '" . join(' or ', $classTypes) . "' implement method: '" . $expression['name'] . "'", $expression);
                             } else {
                                 throw new CompilerException("Class '" . $classTypes[0] . "' does not implement method: '" . $expression['name'] . "'", $expression);
                             }
                         } else {
                             // @TODO, raise an exception here?
                         }
                     }
                 }
             }
         }
     }
     if (isset($method)) {
         $this->_reflection = $method;
     }
     /**
      * Transfer the return type-hint to the returned variable
      */
     if ($isExpecting) {
         if (isset($method)) {
             if ($method instanceof ClassMethod) {
                 if ($method->isVoid()) {
                     throw new CompilerException("Method '" . $classDefinition->getCompleteName() . "::" . $expression['name'] . "' is marked as 'void' and it does not return anything", $expression);
                 }
                 $returnClassTypes = $method->getReturnClassTypes();
                 if ($returnClassTypes !== null) {
                     $symbolVariable->setDynamicTypes('object');
                     foreach ($returnClassTypes as &$returnClassType) {
                         $returnClassType = $compilationContext->getFullName($returnClassType);
                     }
                     $symbolVariable->setClassTypes($returnClassTypes);
                 }
                 $returnTypes = $method->getReturnTypes();
                 if ($returnTypes !== null) {
                     foreach ($returnTypes as $dataType => $returnType) {
                         $symbolVariable->setDynamicTypes($dataType);
                     }
                 }
             }
         }
     }
     /**
      * Some parameters in internal methods receive parameters as references
      */
     if (isset($expression['parameters'])) {
         $references = array();
         if ($type == self::CALL_NORMAL || $type == self::CALL_DYNAMIC_STRING) {
             if (isset($method)) {
                 if ($method instanceof \ReflectionMethod) {
                     $position = 0;
                     foreach ($method->getParameters() as $parameter) {
                         if ($parameter->isPassedByReference()) {
                             $references[$position] = true;
                         }
                         $position++;
                     }
                 }
             }
         }
     }
     /**
      * Include fcall header
      */
     $compilationContext->headersManager->add('kernel/fcall');
     /**
      * Call methods must grown the stack
      */
     $compilationContext->symbolTable->mustGrownStack(true);
     /**
      * Mark references
      */
     if (isset($expression['parameters'])) {
         $params = $this->getResolvedParams($expression['parameters'], $compilationContext, $expression, isset($method) ? $method : null);
         if (count($references)) {
             foreach ($params as $position => $param) {
                 if (isset($references[$position])) {
                     $compilationContext->codePrinter->output('Z_SET_ISREF_P(' . $param . ');');
                 }
             }
         }
         // We check here if a correct parameter type is passed to the called method
         if ($type == self::CALL_NORMAL) {
             if (isset($method) && $method instanceof ClassMethod && isset($expression['parameters'])) {
                 $resolvedTypes = $this->getResolvedTypes();
                 $resolvedDynamicTypes = $this->getResolvedDynamicTypes();
                 //$typeInference = $method->getStaticTypeInferencePass();
                 foreach ($method->getParameters() as $n => $parameter) {
                     if (isset($parameter['data-type'])) {
                         if (!isset($resolvedTypes[$n])) {
                             continue;
                         }
                         /**
                          * If the passed parameter is different to the expected type we show a warning
                          */
                         if ($resolvedTypes[$n] != $parameter['data-type']) {
                             switch ($resolvedTypes[$n]) {
                                 case 'bool':
                                 case 'boolean':
                                     switch ($parameter['data-type']) {
                                         /* compatible types */
                                         case 'bool':
                                         case 'boolean':
                                         case 'variable':
                                             break;
                                         default:
                                             $compilationContext->logger->warning("Passing possible incorrect type for parameter: " . $classDefinition->getCompleteName() . '::' . $method->getName() . '(' . $parameter['name'] . '), passing: ' . $resolvedDynamicTypes[$n] . ', ' . "expecting: " . $parameter['data-type'], "possible-wrong-parameter", $expression);
                                             break;
                                     }
                                     break;
                                 case 'array':
                                     switch ($parameter['data-type']) {
                                         /* compatible types */
                                         case 'array':
                                         case 'variable':
                                             break;
                                         case 'callable':
                                             /**
                                              * Array can be a callable type, example: [$this, "method"]
                                              *
                                              * @todo we need to check this array if can...
                                              */
                                             break;
                                         default:
                                             $compilationContext->logger->warning("Passing possible incorrect type for parameter: " . $classDefinition->getCompleteName() . '::' . $method->getName() . '(' . $parameter['name'] . '), passing: ' . $resolvedDynamicTypes[$n] . ', ' . "expecting: " . $parameter['data-type'], "possible-wrong-parameter", $expression);
                                             break;
                                     }
                                     break;
                                 case 'callable':
                                     switch ($parameter['data-type']) {
                                         /* compatible types */
                                         case 'callable':
                                         case 'variable':
                                             break;
                                         default:
                                             $compilationContext->logger->warning("Passing possible incorrect type for parameter: " . $classDefinition->getCompleteName() . '::' . $method->getName() . '(' . $parameter['name'] . '), passing: ' . $resolvedDynamicTypes[$n] . ', ' . "expecting: " . $parameter['data-type'], "possible-wrong-parameter", $expression);
                                             break;
                                     }
                                     break;
                                 case 'string':
                                     switch ($parameter['data-type']) {
                                         /* compatible types */
                                         case 'string':
                                         case 'variable':
                                             break;
                                         default:
                                             $compilationContext->logger->warning("Passing possible incorrect type for parameter: " . $classDefinition->getCompleteName() . '::' . $method->getName() . '(' . $parameter['name'] . '), passing: ' . $resolvedDynamicTypes[$n] . ', ' . "expecting: " . $parameter['data-type'], "possible-wrong-parameter", $expression);
                                             break;
                                     }
                                     break;
                                     /**
                                      * Passing polymorphic variables to static typed parameters
                                      * could lead to potential unexpected type coercions
                                      */
                                 /**
                                  * Passing polymorphic variables to static typed parameters
                                  * could lead to potential unexpected type coercions
                                  */
                                 case 'variable':
                                     if ($resolvedDynamicTypes[$n] != $parameter['data-type']) {
                                         if ($resolvedDynamicTypes[$n] == 'undefined') {
                                             $compilationContext->logger->warning("Passing possible incorrect type to parameter: " . $classDefinition->getCompleteName() . '::' . $parameter[$n]['name'] . ', passing: ' . $resolvedDynamicTypes[$n] . ', ' . "expecting: " . $parameter[$n]['data-type'], "possible-wrong-parameter-undefined", $expression);
                                         }
                                         //echo '1: ', $resolvedTypes[$n], ' ', $resolvedDynamicTypes[$n], ' ', $parameter[0]['data-type'], ' ', PHP_EOL;
                                     }
                                     break;
                             }
                         }
                     }
                 }
             }
         }
     } else {
         $params = array();
     }
     // Add the last call status to the current symbol table
     $this->addCallStatusFlag($compilationContext);
     // Initialize non-temporary variables
     if ($mustInit) {
         $symbolVariable->setMustInitNull(true);
         $symbolVariable->trackVariant($compilationContext);
     }
     // Generate the code according to the call type
     if ($type == self::CALL_NORMAL || $type == self::CALL_DYNAMIC_STRING) {
         $realMethod = $this->getRealCalledMethod($compilationContext, $variableVariable, $methodName);
         $isInternal = false;
         if (is_object($realMethod[1])) {
             $isInternal = $realMethod[1]->isInternal();
             if ($isInternal && $realMethod[0] > 1) {
                 throw new CompilerException("Cannot resolve method: '" . $expression['name'] . "' in polymorphic variable", $expression);
             }
         }
         if (!$isInternal) {
             // Check if the method call can have an inline cache
             $methodCache = $compilationContext->cacheManager->getMethodCache();
             $cachePointer = $methodCache->get($compilationContext, $methodName, $variableVariable);
             if (!count($params)) {
                 if ($isExpecting) {
                     if ($symbolVariable->getName() == 'return_value') {
                         $codePrinter->output('ZEPHIR_RETURN_CALL_METHOD(' . $variableVariable->getName() . ', "' . $methodName . '", ' . $cachePointer . ');');
                     } else {
                         $codePrinter->output('ZEPHIR_CALL_METHOD(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', "' . $methodName . '", ' . $cachePointer . ');');
                     }
                 } else {
                     $codePrinter->output('ZEPHIR_CALL_METHOD(NULL, ' . $variableVariable->getName() . ', "' . $methodName . '", ' . $cachePointer . ');');
                 }
             } else {
                 if ($isExpecting) {
                     if ($symbolVariable->getName() == 'return_value') {
                         $codePrinter->output('ZEPHIR_RETURN_CALL_METHOD(' . $variableVariable->getName() . ', "' . $methodName . '", ' . $cachePointer . ', ' . join(', ', $params) . ');');
                     } else {
                         $codePrinter->output('ZEPHIR_CALL_METHOD(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', "' . $methodName . '", ' . $cachePointer . ', ' . join(', ', $params) . ');');
                     }
                 } else {
                     $codePrinter->output('ZEPHIR_CALL_METHOD(NULL, ' . $variableVariable->getName() . ', "' . $methodName . '", ' . $cachePointer . ', ' . join(', ', $params) . ');');
                 }
             }
         } else {
             if (!count($params)) {
                 if ($isExpecting) {
                     if ($symbolVariable->getName() == 'return_value') {
                         $codePrinter->output('ZEPHIR_RETURN_CALL_INTERNAL_METHOD_P0(' . $variableVariable->getName() . ', ' . $method->getInternalName() . ');');
                     } else {
                         $codePrinter->output('ZEPHIR_CALL_INTERNAL_METHOD_P0(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', ' . $method->getInternalName() . ');');
                     }
                 } else {
                     $codePrinter->output('ZEPHIR_CALL_INTERNAL_METHOD_NORETURN_P0(' . $variableVariable->getName() . ', ' . $method->getInternalName() . ');');
                 }
             } else {
                 if ($isExpecting) {
                     if ($symbolVariable->getName() == 'return_value') {
                         $codePrinter->output('ZEPHIR_RETURN_CALL_INTERNAL_METHOD_P' . count($params) . '(' . $variableVariable->getName() . ', ' . $method->getInternalName() . ', ' . join(', ', $params) . ');');
                     } else {
                         $codePrinter->output('ZEPHIR_CALL_INTERNAL_METHOD_P' . count($params) . '(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', ' . $method->getInternalName() . ', ' . join(', ', $params) . ');');
                     }
                 } else {
                     $codePrinter->output('ZEPHIR_CALL_INTERNAL_METHOD_NORETURN_P' . count($params) . '(' . $variableVariable->getName() . ', ' . $method->getInternalName() . ', ' . join(', ', $params) . ');');
                 }
             }
         }
     } else {
         if ($type == self::CALL_DYNAMIC) {
             switch ($variableMethod->getType()) {
                 case 'string':
                 case 'variable':
                     break;
                 default:
                     throw new Exception('Cannot use variable type: ' . $variableMethod->getType() . ' as method caller');
             }
             $cachePointer = 'NULL, 0';
             if (!count($params)) {
                 if ($isExpecting) {
                     if ($symbolVariable->getName() == 'return_value') {
                         $codePrinter->output('ZEPHIR_RETURN_CALL_METHOD_ZVAL(' . $variableVariable->getName() . ', ' . $variableMethod->getName() . ', ' . $cachePointer . ');');
                     } else {
                         $codePrinter->output('ZEPHIR_CALL_METHOD_ZVAL(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', ' . $variableMethod->getName() . ',  ' . $cachePointer . ');');
                     }
                 } else {
                     $codePrinter->output('ZEPHIR_CALL_METHOD_ZVAL(NULL, ' . $variableVariable->getName() . ', ' . $variableMethod->getName() . ', ' . $cachePointer . ');');
                 }
             } else {
                 if ($isExpecting) {
                     if ($symbolVariable->getName() == 'return_value') {
                         $codePrinter->output('ZEPHIR_RETURN_CALL_METHOD_ZVAL(' . $variableVariable->getName() . ', ' . $variableMethod->getName() . ', ' . $cachePointer . ', ' . join(', ', $params) . ');');
                     } else {
                         $codePrinter->output('ZEPHIR_CALL_METHOD_ZVAL(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', ' . $variableMethod->getName() . ', ' . $cachePointer . ', ' . join(', ', $params) . ');');
                     }
                 } else {
                     $codePrinter->output('ZEPHIR_CALL_METHOD_ZVAL(NULL, ' . $variableVariable->getName() . ', ' . $variableMethod->getName() . ', ' . $cachePointer . ', ' . join(', ', $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 . ');');
     }
     // We can mark temporary variables generated as idle
     foreach ($this->getTemporalVariables() as $tempVariable) {
         $tempVariable->setIdle(true);
     }
     // Release parameters marked as references
     if (isset($expression['parameters'])) {
         if (count($references)) {
             foreach ($params as $position => $param) {
                 if (isset($references[$position])) {
                     $compilationContext->codePrinter->output('Z_UNSET_ISREF_P(' . $param . ');');
                 }
             }
         }
     }
     $this->addCallStatusOrJump($compilationContext);
     if ($isExpecting) {
         return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression);
     }
     return new CompiledExpression('null', null, $expression);
 }
Ejemplo n.º 3
0
 /**
  * Compiles a static method call
  *
  * @param array $expr
  * @param CompilationContext $compilationContext
  */
 public function compile(Expression $expr, CompilationContext $compilationContext)
 {
     $expression = $expr->getExpression();
     $methodName = strtolower($expression['name']);
     if (isset($expression['dynamic'])) {
         $dynamicMethod = $expression['dynamic'];
     } else {
         $dynamicMethod = false;
     }
     $symbolVariable = null;
     /**
      * Create temporary variable if needed
      */
     $mustInit = false;
     $isExpecting = $expr->isExpectingReturn();
     if ($isExpecting) {
         $symbolVariable = $expr->getExpectingVariable();
         if (is_object($symbolVariable)) {
             $readDetector = new ReadDetector($expression);
             if ($readDetector->detect($symbolVariable->getName(), $expression)) {
                 $symbolVariable = $compilationContext->symbolTable->getTempVariableForObserveOrNullify('variable', $compilationContext, $expression);
             } else {
                 $mustInit = true;
             }
         } else {
             $symbolVariable = $compilationContext->symbolTable->getTempVariableForObserveOrNullify('variable', $compilationContext, $expression);
         }
     }
     /**
      * Method calls only return zvals so we need to validate the target variable is also a zval
      */
     if ($isExpecting) {
         /**
          * At this point, we don't know the exact dynamic type returned by the static method call
          */
         $symbolVariable->setDynamicTypes('undefined');
         if ($symbolVariable->getType() != 'variable') {
             throw new CompilerException("Returned values by functions can only be assigned to variant variables", $expression);
         }
     }
     /**
      * Include fcall header
      */
     $compilationContext->headersManager->add('kernel/fcall');
     $compiler = $compilationContext->compiler;
     $dynamicClass = $expression['dynamic-class'];
     if (!$dynamicClass) {
         $className = $expression['class'];
         $classDefinition = false;
         if ($className != 'self' && $className != 'parent') {
             if (is_string($className)) {
                 $className = $compilationContext->getFullName($className);
                 if ($compiler->isClass($className)) {
                     $classDefinition = $compiler->getClassDefinition($className);
                 } else {
                     if ($compiler->isInternalClass($className)) {
                         $classDefinition = $compiler->getInternalClassDefinition($className);
                     } else {
                         throw new CompilerException("Class name: " . $className . " does not exist", $expression);
                     }
                 }
             } else {
                 foreach ($className as $singleClass) {
                     $className = $compilationContext->getFullName($singleClass);
                     if ($compiler->isClass($singleClass)) {
                         $classDefinition = $compiler->getClassDefinition($singleClass);
                     } else {
                         throw new CompilerException("Class name: " . $className . " does not exist", $expression);
                     }
                 }
             }
         } else {
             if ($className == 'self') {
                 $classDefinition = $compilationContext->classDefinition;
             } else {
                 if ($className == 'parent') {
                     $classDefinition = $compilationContext->classDefinition;
                     $extendsClass = $classDefinition->getExtendsClass();
                     if (!$extendsClass) {
                         throw new CompilerException('Cannot call method "' . $methodName . '" on parent because class ' . $classDefinition->getCompleteName() . ' does not extend any class', $expression);
                     }
                     $currentClassDefinition = $classDefinition;
                     $classDefinition = $classDefinition->getExtendsClassDefinition();
                 }
             }
         }
     }
     /**
      * Check if the class implements the method
      */
     if (!$dynamicMethod && !$dynamicClass) {
         if (!$classDefinition->hasMethod($methodName)) {
             throw new CompilerException("Class '" . $classDefinition->getCompleteName() . "' does not implement static method: '" . $expression['name'] . "'", $expression);
         } else {
             $method = $classDefinition->getMethod($methodName);
             if ($method->isPrivate() && $method->getClassDefinition() != $compilationContext->classDefinition) {
                 throw new CompilerException("Cannot call private method '" . $methodName . "' out of its scope", $expression);
             }
             if ($className != 'parent' && $className != 'self') {
                 if (!$method->isStatic()) {
                     throw new CompilerException("Cannot call non-static method '" . $methodName . "' in a static way", $expression);
                 }
             }
             if (!$classDefinition->hasMethod("__callStatic")) {
                 if ($method instanceof ClassMethod && !$method->isInternal()) {
                     /**
                      * Try to produce an exception if method is called with a wrong number of parameters
                      */
                     if (isset($expression['parameters'])) {
                         $callNumberParameters = count($expression['parameters']);
                     } else {
                         $callNumberParameters = 0;
                     }
                     $classMethod = $classDefinition->getMethod($methodName);
                     $expectedNumberParameters = $classMethod->getNumberOfRequiredParameters();
                     if (!$expectedNumberParameters && $callNumberParameters > 0) {
                         $numberParameters = $classMethod->getNumberOfParameters();
                         if ($callNumberParameters > $numberParameters) {
                             throw new CompilerException("Method '" . $classDefinition->getCompleteName() . "::" . $expression['name'] . "' called with a wrong number of parameters, the method has: " . $expectedNumberParameters . ", passed: " . $callNumberParameters, $expression);
                         }
                     }
                     if ($callNumberParameters < $expectedNumberParameters) {
                         throw new CompilerException("Method '" . $classDefinition->getCompleteName() . "::" . $expression['name'] . "' called with a wrong number of parameters, the method has: " . $expectedNumberParameters . ", passed: " . $callNumberParameters, $expression);
                     }
                 }
             } else {
                 if (!isset($method)) {
                     $method = $classDefinition->getMethod("__callStatic");
                     if ($method->isPrivate() && $method->getClassDefinition() != $compilationContext->classDefinition) {
                         throw new CompilerException("Cannot call private magic method '__call' out of its scope", $expression);
                     }
                 }
             }
         }
     }
     /**
      * Call static methods in the same class, use the special context 'self'
      * Call static methods in the 'self' context
      */
     if (!$dynamicMethod) {
         if ($dynamicClass) {
             $this->callFromDynamicClass($methodName, $expression, $symbolVariable, $mustInit, $isExpecting, $compilationContext);
         } else {
             if ($className == 'self' || $classDefinition == $compilationContext->classDefinition) {
                 $this->callSelf($methodName, $expression, $symbolVariable, $mustInit, $isExpecting, $classDefinition, $compilationContext, isset($method) ? $method : null);
             } else {
                 if ($className == 'parent') {
                     $this->callParent($methodName, $expression, $symbolVariable, $mustInit, $isExpecting, $currentClassDefinition, $compilationContext, isset($method) ? $method : null);
                 } else {
                     $this->callFromClass($methodName, $expression, $symbolVariable, $mustInit, $isExpecting, $classDefinition, $compilationContext, isset($method) ? $method : null);
                 }
             }
         }
     } else {
         if ($dynamicClass) {
             $this->callFromDynamicClassDynamicMethod($expression, $symbolVariable, $mustInit, $isExpecting, $compilationContext);
         }
     }
     /**
      * Add the last call status to the current symbol table
      */
     $this->addCallStatusFlag($compilationContext);
     /**
      * Transfer the return type-hint to the returned variable
      */
     if ($isExpecting) {
         if (isset($method)) {
             if ($method instanceof ClassMethod) {
                 $returnClassTypes = $method->getReturnClassTypes();
                 if ($returnClassTypes !== null) {
                     $symbolVariable->setDynamicTypes('object');
                     foreach ($returnClassTypes as $classType) {
                         $symbolVariable->setClassTypes($compilationContext->getFullName($classType));
                     }
                 }
                 $returnTypes = $method->getReturnTypes();
                 if ($returnTypes !== null) {
                     foreach ($returnTypes as $dataType => $returnType) {
                         $symbolVariable->setDynamicTypes($dataType);
                     }
                 }
             }
         }
     }
     /**
      * We can mark temporary variables generated as idle here
      */
     foreach ($this->getTemporalVariables() as $tempVariable) {
         $tempVariable->setIdle(true);
     }
     if ($isExpecting) {
         return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression);
     }
     return new CompiledExpression('null', null, $expression);
 }
Ejemplo n.º 4
0
 /**
  * @param CompilationContext $compilationContext
  */
 public function compile(CompilationContext $compilationContext)
 {
     $exprRaw = $this->_statement['expr'];
     $expr = new EvalExpression();
     $condition = $expr->optimize($exprRaw, $compilationContext);
     /**
      * This pass tries to move dynamic variable initialization out of the if/else branch
      */
     if (isset($this->_statement['statements']) && (isset($this->_statement['else_statements']) || isset($this->_statement['elseif_statements']))) {
         $readDetector = new ReadDetector();
         $skipVariantInit = new SkipVariantInit();
         $skipVariantInit->setVariablesToSkip(0, $expr->getUsedVariables());
         $skipVariantInit->pass(0, new StatementsBlock($this->_statement['statements']));
         $lastBranchId = 0;
         if (isset($this->_statement['else_statements'])) {
             ++$lastBranchId;
             $skipVariantInit->setVariablesToSkip($lastBranchId, $expr->getUsedVariables());
             $skipVariantInit->pass($lastBranchId, new StatementsBlock($this->_statement['else_statements']));
         }
         if (isset($this->_statement['elseif_statements'])) {
             foreach ($this->_statement['elseif_statements'] as $key => $statement) {
                 $this->_statement['elseif_statements'][$key]['condition'] = $expr->optimize($statement['expr'], $compilationContext);
                 $lastBranchId++;
                 $skipVariantInit->setVariablesToSkip($lastBranchId, $expr->getUsedVariables());
                 if (!isset($statement['statements'])) {
                     continue;
                 }
                 $skipVariantInit->pass($lastBranchId, new StatementsBlock($statement));
             }
         }
         $symbolTable = $compilationContext->symbolTable;
         foreach ($skipVariantInit->getVariables() as $variable) {
             if ($symbolTable->hasVariable($variable)) {
                 $symbolVariable = $symbolTable->getVariable($variable);
                 if ($symbolVariable->getType() == 'variable') {
                     if (!$readDetector->detect($variable, $exprRaw)) {
                         $symbolVariable->initVariant($compilationContext);
                         $symbolVariable->skipInitVariant(2);
                     }
                 }
             }
         }
     }
     $compilationContext->codePrinter->output('if (' . $condition . ') {');
     $this->_evalExpression = $expr;
     /**
      * Try to mark latest temporary variable used as idle
      */
     $evalVariable = $expr->getEvalVariable();
     if (is_object($evalVariable)) {
         if ($evalVariable->isTemporal()) {
             $evalVariable->setIdle(true);
         }
     }
     /**
      * Compile statements in the 'if' block
      */
     if (isset($this->_statement['statements'])) {
         $st = new StatementsBlock($this->_statement['statements']);
         $branch = $st->compile($compilationContext, $expr->isUnreachable(), Branch::TYPE_CONDITIONAL_TRUE);
         $branch->setRelatedStatement($this);
     }
     /**
      * Compile statements in the 'elseif' block
      */
     if (isset($this->_statement['elseif_statements'])) {
         foreach ($this->_statement['elseif_statements'] as $key => $statement) {
             if (!isset($statement['statements'])) {
                 continue;
             }
             $st = new StatementsBlock($statement['statements']);
             $compilationContext->codePrinter->output('} else if (' . $statement['condition'] . ') {');
             $branch = $st->compile($compilationContext, $expr->isUnreachable(), Branch::TYPE_CONDITIONAL_TRUE);
             $branch->setRelatedStatement($this);
         }
     }
     /**
      * Compile statements in the 'else' block
      */
     if (isset($this->_statement['else_statements'])) {
         $compilationContext->codePrinter->output('} else {');
         $st = new StatementsBlock($this->_statement['else_statements']);
         $branch = $st->compile($compilationContext, $expr->isUnreachableElse(), Branch::TYPE_CONDITIONAL_FALSE);
         $branch->setRelatedStatement($this);
     }
     $compilationContext->codePrinter->output('}');
 }
Ejemplo n.º 5
0
 /**
  * Compiles foo = {expr}
  * Changes the value of a mutable variable
  *
  * @param $variable
  * @param Variable $symbolVariable
  * @param CompiledExpression $resolvedExpr
  * @param ReadDetector $readDetector
  * @param CompilationContext $compilationContext
  * @param $statement
  * @throws CompilerException
  */
 public function assign($variable, ZephirVariable $symbolVariable, CompiledExpression $resolvedExpr, ReadDetector $readDetector, CompilationContext $compilationContext, $statement)
 {
     if ($symbolVariable->isReadOnly()) {
         throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is read only", $statement);
     }
     $codePrinter = $compilationContext->codePrinter;
     /**
      * Only initialize variables if it's direct assignment
      */
     if ($statement['operator'] == 'assign') {
         $symbolVariable->setIsInitialized(true, $compilationContext, $statement);
     } else {
         if (!$symbolVariable->isInitialized()) {
             throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is not initialized", $statement);
         }
     }
     /**
      * Set the assigned value to the variable as a CompiledExpression
      * We could use this expression for further analysis
      */
     $symbolVariable->setPossibleValue($resolvedExpr, $compilationContext);
     $type = $symbolVariable->getType();
     switch ($type) {
         case 'int':
         case 'uint':
         case 'long':
         case 'ulong':
         case 'char':
         case 'uchar':
             switch ($resolvedExpr->getType()) {
                 case 'null':
                     switch ($statement['operator']) {
                         case 'assign':
                             $codePrinter->output($variable . ' = 0;');
                             break;
                         case 'add-assign':
                             $codePrinter->output($variable . ' += 0;');
                             break;
                         case 'sub-assign':
                             $codePrinter->output($variable . ' -= 0;');
                             break;
                         case 'mul-assign':
                             $codePrinter->output($variable . ' *= 0;');
                             break;
                         default:
                             throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: int", $statement);
                     }
                     break;
                 case 'int':
                 case 'uint':
                 case 'long':
                 case 'ulong':
                     switch ($statement['operator']) {
                         case 'assign':
                             $codePrinter->output($variable . ' = ' . $resolvedExpr->getCode() . ';');
                             break;
                         case 'add-assign':
                             $codePrinter->output($variable . ' += ' . $resolvedExpr->getCode() . ';');
                             break;
                         case 'sub-assign':
                             $codePrinter->output($variable . ' -= ' . $resolvedExpr->getCode() . ';');
                             break;
                         case 'mul-assign':
                             $codePrinter->output($variable . ' *= ' . $resolvedExpr->getCode() . ';');
                             break;
                         case 'div-assign':
                             $codePrinter->output($variable . ' /= ' . $resolvedExpr->getCode() . ';');
                             break;
                         case 'mod-assign':
                             $codePrinter->output($variable . ' %= ' . $resolvedExpr->getCode() . ';');
                             break;
                         default:
                             throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: int", $statement);
                     }
                     break;
                 case 'char':
                 case 'uchar':
                     switch ($statement['operator']) {
                         case 'assign':
                             $codePrinter->output($variable . ' = \'' . $resolvedExpr->getCode() . '\';');
                             break;
                         case 'add-assign':
                             $codePrinter->output($variable . ' += \'' . $resolvedExpr->getCode() . '\';');
                             break;
                         case 'sub-assign':
                             $codePrinter->output($variable . ' -= \'' . $resolvedExpr->getCode() . '\';');
                             break;
                         case 'mul-assign':
                             $codePrinter->output($variable . ' *= \'' . $resolvedExpr->getCode() . '\';');
                             break;
                         default:
                             throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: int", $statement);
                     }
                     break;
                 case 'double':
                     switch ($statement['operator']) {
                         case 'assign':
                             $codePrinter->output($variable . ' = (long) (' . $resolvedExpr->getCode() . ');');
                             break;
                         case 'add-assign':
                             $codePrinter->output($variable . ' += (long) (' . $resolvedExpr->getCode() . ');');
                             break;
                         case 'sub-assign':
                             $codePrinter->output($variable . ' -= (long) (' . $resolvedExpr->getCode() . ');');
                             break;
                         case 'mul-assign':
                             $codePrinter->output($variable . ' *= (long) (' . $resolvedExpr->getCode() . ');');
                             break;
                         default:
                             throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: int", $statement);
                     }
                     break;
                 case 'bool':
                     switch ($statement['operator']) {
                         case 'assign':
                             $codePrinter->output($variable . ' = ' . $resolvedExpr->getBooleanCode() . ';');
                             break;
                         case 'add-assign':
                             $codePrinter->output($variable . ' += ' . $resolvedExpr->getBooleanCode() . ';');
                             break;
                         case 'sub-assign':
                             $codePrinter->output($variable . ' -= ' . $resolvedExpr->getBooleanCode() . ';');
                             break;
                         default:
                             throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: int", $statement);
                     }
                     break;
                 case 'variable':
                     $itemVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement);
                     switch ($itemVariable->getType()) {
                         case 'int':
                         case 'uint':
                         case 'long':
                         case 'ulong':
                         case 'bool':
                         case 'char':
                         case 'uchar':
                             switch ($statement['operator']) {
                                 case 'assign':
                                     $codePrinter->output($variable . ' = ' . $itemVariable->getName() . ';');
                                     break;
                                 case 'add-assign':
                                     $codePrinter->output($variable . ' += ' . $itemVariable->getName() . ';');
                                     break;
                                 case 'sub-assign':
                                     $codePrinter->output($variable . ' -= ' . $itemVariable->getName() . ';');
                                     break;
                                 case 'mul-assign':
                                     $codePrinter->output($variable . ' *= ' . $itemVariable->getName() . ';');
                                     break;
                                 default:
                                     throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: int", $statement);
                             }
                             break;
                         case 'double':
                             switch ($statement['operator']) {
                                 case 'assign':
                                     $codePrinter->output($variable . ' = (long) ' . $itemVariable->getName() . ';');
                                     break;
                                 case 'add-assign':
                                     $codePrinter->output($variable . ' += (long) ' . $itemVariable->getName() . ';');
                                     break;
                                 case 'sub-assign':
                                     $codePrinter->output($variable . ' -= (long) ' . $itemVariable->getName() . ';');
                                     break;
                                 case 'mul-assign':
                                     $codePrinter->output($variable . ' *= (long) ' . $itemVariable->getName() . ';');
                                     break;
                                 default:
                                     throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: int", $statement);
                             }
                             break;
                         case 'variable':
                             $compilationContext->headersManager->add('kernel/operators');
                             switch ($statement['operator']) {
                                 case 'assign':
                                     $codePrinter->output($variable . ' = zephir_get_numberval(' . $resolvedExpr->resolve(null, $compilationContext) . ');');
                                     break;
                                 case 'add-assign':
                                     $codePrinter->output($variable . ' += zephir_get_numberval(' . $resolvedExpr->resolve(null, $compilationContext) . ');');
                                     break;
                                 case 'sub-assign':
                                     $codePrinter->output($variable . ' -= zephir_get_numberval(' . $resolvedExpr->resolve(null, $compilationContext) . ');');
                                     break;
                                 case 'mul-assign':
                                     $codePrinter->output($variable . ' *= zephir_get_numberval(' . $resolvedExpr->resolve(null, $compilationContext) . ');');
                                     break;
                                 default:
                                     throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: int", $statement);
                             }
                             break;
                         default:
                             throw new CompilerException("Unknown type: " . $itemVariable->getType(), $statement);
                     }
                     break;
                 default:
                     throw new CompilerException("Value type '" . $resolvedExpr->getType() . "' cannot be assigned to variable: int", $statement);
             }
             break;
         case 'double':
             switch ($resolvedExpr->getType()) {
                 case 'null':
                     switch ($statement['operator']) {
                         case 'assign':
                             $codePrinter->output($variable . ' = 0.0;');
                             break;
                         case 'add-assign':
                             $codePrinter->output($variable . ' += 0.0;');
                             break;
                         case 'sub-assign':
                             $codePrinter->output($variable . ' -= 0.0;');
                             break;
                         case 'mul-assign':
                             $codePrinter->output($variable . ' *= 0.0;');
                             break;
                         default:
                             throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: double", $statement);
                     }
                     break;
                 case 'int':
                 case 'uint':
                 case 'long':
                 case 'ulong':
                     switch ($statement['operator']) {
                         case 'assign':
                             $codePrinter->output($variable . ' = (double) (' . $resolvedExpr->getCode() . ');');
                             break;
                         case 'add-assign':
                             $codePrinter->output($variable . ' += (double) (' . $resolvedExpr->getCode() . ');');
                             break;
                         case 'sub-assign':
                             $codePrinter->output($variable . ' -= (double) (' . $resolvedExpr->getCode() . ');');
                             break;
                         case 'mul-assign':
                             $codePrinter->output($variable . ' *= (double) (' . $resolvedExpr->getCode() . ');');
                             break;
                         default:
                             throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: double", $statement);
                     }
                     break;
                 case 'double':
                     switch ($statement['operator']) {
                         case 'assign':
                             $codePrinter->output($variable . ' = ' . $resolvedExpr->getCode() . ';');
                             break;
                         case 'add-assign':
                             $codePrinter->output($variable . ' += ' . $resolvedExpr->getCode() . ';');
                             break;
                         case 'sub-assign':
                             $codePrinter->output($variable . ' -= ' . $resolvedExpr->getCode() . ';');
                             break;
                         case 'mul-assign':
                             $codePrinter->output($variable . ' *= ' . $resolvedExpr->getCode() . ';');
                             break;
                         default:
                             throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: double", $statement);
                     }
                     break;
                 case 'bool':
                     switch ($statement['operator']) {
                         case 'assign':
                             $codePrinter->output($variable . ' = ' . $resolvedExpr->getBooleanCode() . ';');
                             break;
                         case 'add-assign':
                             $codePrinter->output($variable . ' += ' . $resolvedExpr->getBooleanCode() . ';');
                             break;
                         case 'sub-assign':
                             $codePrinter->output($variable . ' -= ' . $resolvedExpr->getBooleanCode() . ';');
                             break;
                         case 'mul-assign':
                             $codePrinter->output($variable . ' *= ' . $resolvedExpr->getBooleanCode() . ';');
                             break;
                         default:
                             throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: double", $statement);
                     }
                     break;
                 case 'variable':
                     $itemVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement);
                     switch ($itemVariable->getType()) {
                         case 'int':
                         case 'uint':
                         case 'long':
                         case 'ulong':
                         case 'bool':
                             switch ($statement['operator']) {
                                 case 'assign':
                                     $codePrinter->output($variable . ' = (double) ' . $itemVariable->getName() . ';');
                                     break;
                                 case 'add-assign':
                                     $codePrinter->output($variable . ' += (double) ' . $itemVariable->getName() . ';');
                                     break;
                                 case 'sub-assign':
                                     $codePrinter->output($variable . ' -= (double) ' . $itemVariable->getName() . ';');
                                     break;
                                 case 'mul-assign':
                                     $codePrinter->output($variable . ' *= (double) ' . $itemVariable->getName() . ';');
                                     break;
                                 default:
                                     throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: double", $statement);
                             }
                             break;
                         case 'double':
                             switch ($statement['operator']) {
                                 case 'assign':
                                     $codePrinter->output($variable . ' = ' . $itemVariable->getName() . ';');
                                     break;
                                 case 'add-assign':
                                     $codePrinter->output($variable . ' += ' . $itemVariable->getName() . ';');
                                     break;
                                 case 'sub-assign':
                                     $codePrinter->output($variable . ' -= ' . $itemVariable->getName() . ';');
                                     break;
                                 case 'mul-assign':
                                     $codePrinter->output($variable . ' *= ' . $itemVariable->getName() . ';');
                                     break;
                                 default:
                                     throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: double", $statement);
                             }
                             break;
                         case 'variable':
                             $compilationContext->headersManager->add('kernel/operators');
                             switch ($statement['operator']) {
                                 case 'assign':
                                     $codePrinter->output($variable . ' = zephir_get_numberval(' . $itemVariable->getName() . ');');
                                     break;
                                 case 'add-assign':
                                     $codePrinter->output($variable . ' += zephir_get_numberval(' . $itemVariable->getName() . ');');
                                     break;
                                 case 'sub-assign':
                                     $codePrinter->output($variable . ' -= zephir_get_numberval(' . $itemVariable->getName() . ');');
                                     break;
                                 case 'mul-assign':
                                     $codePrinter->output($variable . ' *= zephir_get_numberval(' . $itemVariable->getName() . ');');
                                     break;
                                 default:
                                     throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: double", $statement);
                             }
                             break;
                         default:
                             throw new CompilerException("Unknown type: " . $itemVariable->getType(), $statement);
                     }
                     break;
                 default:
                     throw new CompilerException("Unknown type " . $resolvedExpr->getType(), $statement);
             }
             break;
         case 'array':
             switch ($resolvedExpr->getType()) {
                 case 'variable':
                 case 'array':
                     switch ($statement['operator']) {
                         case 'assign':
                             if ($variable != $resolvedExpr->getCode()) {
                                 $symbolVariable->setMustInitNull(true);
                                 $compilationContext->symbolTable->mustGrownStack(true);
                                 /* Inherit the dynamic type data from the assigned value */
                                 $symbolVariable->setDynamicTypes('array');
                                 $codePrinter->output('ZEPHIR_CPY_WRT(' . $variable . ', ' . $resolvedExpr->getCode() . ');');
                             }
                             break;
                         default:
                             throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $resolvedExpr->getType(), $resolvedExpr->getOriginal());
                     }
                     break;
                 default:
                     throw new CompilerException("You cannot {$statement['operator']} {$resolvedExpr->getType()} for array type", $resolvedExpr->getOriginal());
             }
             break;
         case 'string':
             switch ($resolvedExpr->getType()) {
                 case 'null':
                     switch ($statement['operator']) {
                         case 'assign':
                             $symbolVariable->initVariant($compilationContext);
                             $codePrinter->output('ZVAL_EMPTY_STRING(' . $variable . ');');
                             break;
                         default:
                             throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: string", $statement);
                     }
                     break;
                 case 'string':
                     switch ($statement['operator']) {
                         case 'assign':
                             $symbolVariable->initVariant($compilationContext);
                             if ($resolvedExpr->getCode()) {
                                 $codePrinter->output('ZVAL_STRING(' . $variable . ', "' . $resolvedExpr->getCode() . '", 1);');
                             } else {
                                 $codePrinter->output('ZVAL_EMPTY_STRING(' . $variable . ');');
                             }
                             break;
                         case 'concat-assign':
                             $codePrinter->output('zephir_concat_self_str(&' . $variable . ', "' . $resolvedExpr->getCode() . '", sizeof("' . $resolvedExpr->getCode() . '")-1 TSRMLS_CC);');
                             break;
                         default:
                             throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: string", $statement);
                     }
                     break;
                 case 'char':
                 case 'uchar':
                     switch ($statement['operator']) {
                         case 'assign':
                             $symbolVariable->initVariant($compilationContext);
                             if ($resolvedExpr->getCode()) {
                                 $codePrinter->output('ZVAL_STRING(' . $variable . ', "' . $resolvedExpr->getCode() . '", 1);');
                             } else {
                                 $codePrinter->output('ZVAL_EMPTY_STRING(' . $variable . ');');
                             }
                             break;
                         case 'concat-assign':
                             $compilationContext->headersManager->add('kernel/operators');
                             $codePrinter->output('zephir_concat_self_str(&' . $variable . ', "' . $resolvedExpr->getCode() . '", sizeof("' . $resolvedExpr->getCode() . '")-1 TSRMLS_CC);');
                             break;
                         default:
                             throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: string", $statement);
                     }
                     break;
                 case 'variable':
                     $itemVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement);
                     switch ($itemVariable->getType()) {
                         case 'int':
                         case 'uint':
                         case 'long':
                         case 'ulong':
                             switch ($statement['operator']) {
                                 case 'assign':
                                     $symbolVariable->initVariant($compilationContext);
                                     $compilationContext->headersManager->add('kernel/string');
                                     $codePrinter->output('Z_STRLEN_P(' . $variable . ') = zephir_spprintf(&Z_STRVAL_P(' . $variable . '), 0, "%ld", ' . $itemVariable->getName() . ');');
                                     $codePrinter->output('Z_TYPE_P(' . $variable . ') = IS_STRING;');
                                     break;
                                 case 'concat-assign':
                                     $compilationContext->headersManager->add('kernel/operators');
                                     $codePrinter->output('zephir_concat_self_long(&' . $variable . ', ' . $itemVariable->getName() . ' TSRMLS_CC);');
                                     break;
                                 default:
                                     throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: string", $statement);
                             }
                             break;
                         case 'char':
                         case 'uchar':
                             switch ($statement['operator']) {
                                 case 'assign':
                                     $symbolVariable->initVariant($compilationContext);
                                     $compilationContext->headersManager->add('kernel/string');
                                     $codePrinter->output('Z_STRLEN_P(' . $variable . ') = zephir_spprintf(&Z_STRVAL_P(' . $variable . '), 0, "%c", ' . $itemVariable->getName() . ');');
                                     $codePrinter->output('Z_TYPE_P(' . $variable . ') = IS_STRING;');
                                     break;
                                 case 'concat-assign':
                                     $compilationContext->headersManager->add('kernel/operators');
                                     $codePrinter->output('zephir_concat_self_char(&' . $variable . ', ' . $itemVariable->getName() . ' TSRMLS_CC);');
                                     break;
                                 default:
                                     throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: string", $statement);
                             }
                             break;
                         case 'string':
                             switch ($statement['operator']) {
                                 case 'assign':
                                     $symbolVariable->setMustInitNull(true);
                                     $compilationContext->symbolTable->mustGrownStack(true);
                                     if ($variable != $itemVariable->getName()) {
                                         $codePrinter->output('ZEPHIR_CPY_WRT(' . $variable . ', ' . $itemVariable->getName() . ');');
                                     }
                                     break;
                                 case 'concat-assign':
                                     $compilationContext->headersManager->add('kernel/operators');
                                     $codePrinter->output('zephir_concat_self(&' . $variable . ', ' . $itemVariable->getName() . ' TSRMLS_CC);');
                                     break;
                                 default:
                                     throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: string", $statement);
                             }
                             break;
                         case 'variable':
                             switch ($statement['operator']) {
                                 case 'assign':
                                     $symbolVariable->setMustInitNull(true);
                                     $compilationContext->symbolTable->mustGrownStack(true);
                                     $compilationContext->headersManager->add('kernel/operators');
                                     $codePrinter->output('zephir_get_strval(' . $variable . ', ' . $itemVariable->getName() . ');');
                                     break;
                                 case 'concat-assign':
                                     $compilationContext->headersManager->add('kernel/operators');
                                     $codePrinter->output('zephir_concat_self(&' . $variable . ', ' . $itemVariable->getName() . ' TSRMLS_CC);');
                                     break;
                                 default:
                                     throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $itemVariable->getType(), $statement);
                             }
                             break;
                         default:
                             throw new CompilerException("Unknown type: " . $itemVariable->getType(), $statement);
                     }
                     break;
                 default:
                     throw new CompilerException("Unknown type " . $resolvedExpr->getType(), $statement);
             }
             break;
         case 'bool':
             switch ($resolvedExpr->getType()) {
                 case 'null':
                     switch ($statement['operator']) {
                         case 'assign':
                             $codePrinter->output($variable . ' = 0;');
                             break;
                         default:
                             throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: null", $statement);
                     }
                     break;
                 case 'int':
                 case 'uint':
                 case 'long':
                 case 'ulong':
                 case 'double':
                     switch ($statement['operator']) {
                         case 'assign':
                             $codePrinter->output($variable . ' = (' . $resolvedExpr->getCode() . ') ? 1 : 0;');
                             break;
                         default:
                             throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $resolvedExpr->getType(), $statement);
                     }
                     break;
                 case 'char':
                 case 'uchar':
                     switch ($statement['operator']) {
                         case 'assign':
                             $codePrinter->output($variable . ' = (\'' . $resolvedExpr->getCode() . '\') ? 1 : 0;');
                             break;
                         default:
                             throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $resolvedExpr->getType(), $statement);
                     }
                     break;
                 case 'bool':
                     switch ($statement['operator']) {
                         case 'assign':
                             $codePrinter->output($variable . ' = ' . $resolvedExpr->getBooleanCode() . ';');
                             break;
                         default:
                             throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $resolvedExpr->getType(), $statement);
                     }
                     break;
                 case 'variable':
                     $itemVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement);
                     switch ($itemVariable->getType()) {
                         case 'int':
                         case 'uint':
                         case 'long':
                         case 'ulong':
                         case 'double':
                             switch ($statement['operator']) {
                                 case 'assign':
                                     $codePrinter->output($variable . ' = (' . $itemVariable->getName() . ') ? 1 : 0;');
                                     break;
                                 default:
                                     throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $itemVariable->getType(), $statement);
                             }
                             break;
                         case 'bool':
                             switch ($statement['operator']) {
                                 case 'assign':
                                     $codePrinter->output($variable . ' = ' . $itemVariable->getName() . ';');
                                     break;
                                 default:
                                     throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $itemVariable->getType(), $statement);
                             }
                             break;
                         case 'variable':
                         case 'string':
                         case 'array':
                             switch ($statement['operator']) {
                                 case 'assign':
                                     $codePrinter->output($variable . ' = zephir_is_true(' . $itemVariable->getName() . ');');
                                     break;
                                 default:
                                     throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $itemVariable->getType(), $statement);
                             }
                             break;
                         default:
                             throw new CompilerException("Cannot assign variable: " . $itemVariable->getType(), $statement);
                     }
                     break;
                 default:
                     throw new CompilerException("Unknown type: " . $resolvedExpr->getType(), $statement);
             }
             break;
         case 'variable':
             switch ($resolvedExpr->getType()) {
                 case 'null':
                     switch ($statement['operator']) {
                         case 'assign':
                             $symbolVariable->initVariant($compilationContext);
                             $symbolVariable->setDynamicTypes('null');
                             if ($symbolVariable->isLocalOnly()) {
                                 $codePrinter->output('ZVAL_NULL(&' . $variable . ');');
                             } else {
                                 $codePrinter->output('ZVAL_NULL(' . $variable . ');');
                             }
                             break;
                     }
                     break;
                 case 'int':
                 case 'uint':
                 case 'long':
                 case 'ulong':
                     if ($symbolVariable->isLocalOnly()) {
                         $symbol = '&' . $variable;
                     } else {
                         $symbol = $variable;
                     }
                     switch ($statement['operator']) {
                         case 'mul-assign':
                         case 'sub-assign':
                         case 'add-assign':
                             switch ($statement['operator']) {
                                 case 'mul-assign':
                                     $functionName = 'ZEPHIR_MUL_ASSIGN';
                                     break;
                                 case 'sub-assign':
                                     $functionName = 'ZEPHIR_SUB_ASSIGN';
                                     break;
                                 case 'add-assign':
                                     $functionName = 'ZEPHIR_ADD_ASSIGN';
                                     break;
                             }
                             $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext);
                             $codePrinter->output('ZVAL_LONG(' . $tempVariable->getName() . ', ' . $resolvedExpr->getCode() . ');');
                             $compilationContext->symbolTable->mustGrownStack(true);
                             $compilationContext->headersManager->add('kernel/operators');
                             $codePrinter->output($functionName . '(' . $symbol . ', ' . $tempVariable->getName() . ');');
                             break;
                         case 'assign':
                             $symbolVariable->setDynamicTypes('long');
                             if ($readDetector->detect($variable, $resolvedExpr->getOriginal())) {
                                 $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('int', $compilationContext);
                                 $codePrinter->output($tempVariable->getName() . ' = ' . $resolvedExpr->getCode() . ';');
                                 $symbolVariable->initVariant($compilationContext);
                                 $codePrinter->output('ZVAL_LONG(' . $symbol . ', ' . $tempVariable->getName() . ');');
                             } else {
                                 $symbolVariable->initVariant($compilationContext);
                                 $codePrinter->output('ZVAL_LONG(' . $symbol . ', ' . $resolvedExpr->getCode() . ');');
                             }
                             break;
                         case 'div-assign':
                             $symbolVariable->setDynamicTypes('double');
                             if ($readDetector->detect($variable, $resolvedExpr->getOriginal())) {
                                 $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('double', $compilationContext);
                                 $codePrinter->output($tempVariable->getName() . ' = ' . $resolvedExpr->getCode() . ';');
                                 $symbolVariable->initVariant($compilationContext);
                                 $codePrinter->output('ZVAL_DOUBLE(' . $symbol . ', ' . $tempVariable->getName() . ');');
                             } else {
                                 $symbolVariable->initVariant($compilationContext);
                                 $codePrinter->output('ZVAL_DOUBLE(' . $symbol . ', ' . $resolvedExpr->getCode() . ');');
                             }
                             break;
                         default:
                             throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $resolvedExpr->getType(), $resolvedExpr->getOriginal());
                     }
                     break;
                 case 'char':
                 case 'uchar':
                     if ($symbolVariable->isLocalOnly()) {
                         $symbol = '&' . $variable;
                     } else {
                         $symbol = $variable;
                     }
                     switch ($statement['operator']) {
                         case 'assign':
                             $symbolVariable->setDynamicTypes('long');
                             if ($readDetector->detect($variable, $resolvedExpr->getOriginal())) {
                                 $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('char', $compilationContext);
                                 $codePrinter->output($tempVariable->getName() . ' = ' . $resolvedExpr->getCode() . ';');
                                 $symbolVariable->initVariant($compilationContext);
                                 $codePrinter->output('ZVAL_LONG(' . $symbol . ', ' . $tempVariable->getName() . ');');
                             } else {
                                 $symbolVariable->initVariant($compilationContext);
                                 $codePrinter->output('ZVAL_LONG(' . $symbol . ', \'' . $resolvedExpr->getCode() . '\');');
                             }
                             break;
                         default:
                             throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $resolvedExpr->getType(), $resolvedExpr->getOriginal());
                     }
                     break;
                 case 'double':
                     if ($symbolVariable->isLocalOnly()) {
                         $symbol = '&' . $variable;
                     } else {
                         $symbol = $variable;
                     }
                     switch ($statement['operator']) {
                         case 'assign':
                             $symbolVariable->setDynamicTypes('double');
                             if ($readDetector->detect($variable, $resolvedExpr->getOriginal())) {
                                 $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('double', $compilationContext);
                                 $codePrinter->output($tempVariable->getName() . ' = ' . $resolvedExpr->getCode() . ';');
                                 $symbolVariable->initVariant($compilationContext);
                                 $codePrinter->output('ZVAL_DOUBLE(' . $symbol . ', ' . $tempVariable->getName() . ');');
                             } else {
                                 $symbolVariable->initVariant($compilationContext);
                                 $codePrinter->output('ZVAL_DOUBLE(' . $symbol . ', ' . $resolvedExpr->getCode() . ');');
                             }
                             break;
                         default:
                             throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $resolvedExpr->getType(), $resolvedExpr->getOriginal());
                     }
                     break;
                 case 'bool':
                     if ($symbolVariable->isLocalOnly()) {
                         $symbol = '&' . $variable;
                     } else {
                         $symbol = $variable;
                     }
                     switch ($statement['operator']) {
                         case 'assign':
                             $symbolVariable->setDynamicTypes('bool');
                             if ($resolvedExpr->getCode() == 'true') {
                                 $symbolVariable->initVariant($compilationContext);
                                 $codePrinter->output('ZVAL_BOOL(' . $symbol . ', 1);');
                             } else {
                                 if ($resolvedExpr->getCode() == 'false') {
                                     $symbolVariable->initVariant($compilationContext);
                                     $codePrinter->output('ZVAL_BOOL(' . $symbol . ', 0);');
                                 } else {
                                     if ($readDetector->detect($variable, $resolvedExpr->getOriginal())) {
                                         $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('double', $compilationContext);
                                         $codePrinter->output($tempVariable->getName() . ' = ' . $resolvedExpr->getBooleanCode() . ';');
                                         $symbolVariable->initVariant($compilationContext);
                                         $codePrinter->output('ZVAL_BOOL(' . $symbol . ', ' . $tempVariable->getName() . ');');
                                     } else {
                                         $symbolVariable->initVariant($compilationContext);
                                         $codePrinter->output('ZVAL_BOOL(' . $symbol . ', ' . $resolvedExpr->getBooleanCode() . ');');
                                     }
                                 }
                             }
                             break;
                         default:
                             throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $resolvedExpr->getType(), $resolvedExpr->getOriginal());
                     }
                     break;
                 case 'string':
                     switch ($statement['operator']) {
                         case 'assign':
                             $symbolVariable->initVariant($compilationContext);
                             $symbolVariable->setDynamicTypes('string');
                             if ($symbolVariable->isLocalOnly()) {
                                 $codePrinter->output('ZVAL_STRING(&' . $variable . ', "' . $resolvedExpr->getCode() . '", 1);');
                             } else {
                                 $codePrinter->output('ZVAL_STRING(' . $variable . ', "' . $resolvedExpr->getCode() . '", 1);');
                             }
                             break;
                         case 'concat-assign':
                             $compilationContext->headersManager->add('kernel/operators');
                             $codePrinter->output('zephir_concat_self_str(&' . $variable . ', SL("' . $resolvedExpr->getCode() . '") TSRMLS_CC);');
                             break;
                         default:
                             throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $resolvedExpr->getType(), $resolvedExpr->getOriginal());
                     }
                     break;
                 case 'array':
                     switch ($statement['operator']) {
                         case 'assign':
                             if ($variable != $resolvedExpr->getCode()) {
                                 $symbolVariable->setMustInitNull(true);
                                 $compilationContext->symbolTable->mustGrownStack(true);
                                 /* Inherit the dynamic type data from the assigned value */
                                 $symbolVariable->setDynamicTypes('array');
                                 $codePrinter->output('ZEPHIR_CPY_WRT(' . $variable . ', ' . $resolvedExpr->getCode() . ');');
                             }
                             break;
                         default:
                             throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $resolvedExpr->getType(), $resolvedExpr->getOriginal());
                     }
                     break;
                 case 'variable':
                     $itemVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $resolvedExpr->getOriginal());
                     switch ($itemVariable->getType()) {
                         case 'int':
                         case 'uint':
                         case 'long':
                         case 'ulong':
                         case 'char':
                         case 'uchar':
                             switch ($statement['operator']) {
                                 case 'assign':
                                     $symbolVariable->initVariant($compilationContext);
                                     $symbolVariable->setDynamicTypes('long');
                                     if ($symbolVariable->isLocalOnly()) {
                                         $codePrinter->output('ZVAL_LONG(&' . $variable . ', ' . $itemVariable->getName() . ');');
                                     } else {
                                         $codePrinter->output('ZVAL_LONG(' . $variable . ', ' . $itemVariable->getName() . ');');
                                     }
                                     break;
                                 case 'add-assign':
                                     $compilationContext->headersManager->add('kernel/operators');
                                     $symbolVariable->initVariant($compilationContext);
                                     $symbolVariable->setDynamicTypes('long');
                                     $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite($itemVariable->getType(), $compilationContext);
                                     if ($symbolVariable->isLocalOnly()) {
                                         $codePrinter->output($tempVariable->getName() . ' = zephir_get_numberval(&' . $variable . ');');
                                         $codePrinter->output('ZVAL_LONG(&' . $variable . ', ' . $tempVariable->getName() . ' + ' . $itemVariable->getName() . ');');
                                     } else {
                                         $codePrinter->output($tempVariable->getName() . ' = zephir_get_numberval(' . $variable . ');');
                                         $codePrinter->output('ZVAL_LONG(' . $variable . ', ' . $tempVariable->getName() . ' + ' . $itemVariable->getName() . ');');
                                     }
                                     break;
                                 case 'sub-assign':
                                     $compilationContext->headersManager->add('kernel/operators');
                                     $symbolVariable->initVariant($compilationContext);
                                     $symbolVariable->setDynamicTypes('long');
                                     $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite($itemVariable->getType(), $compilationContext);
                                     if ($symbolVariable->isLocalOnly()) {
                                         $codePrinter->output($tempVariable->getName() . ' = zephir_get_numberval(&' . $variable . ');');
                                         $codePrinter->output('ZVAL_LONG(&' . $variable . ', ' . $tempVariable->getName() . ' - ' . $itemVariable->getName() . ');');
                                     } else {
                                         $codePrinter->output($tempVariable->getName() . ' = zephir_get_numberval(' . $variable . ');');
                                         $codePrinter->output('ZVAL_LONG(' . $variable . ', ' . $tempVariable->getName() . ' - ' . $itemVariable->getName() . ');');
                                     }
                                     break;
                                 default:
                                     throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $itemVariable->getType(), $statement);
                             }
                             break;
                         case 'double':
                             switch ($statement['operator']) {
                                 case 'assign':
                                     $symbolVariable->initVariant($compilationContext);
                                     $symbolVariable->setDynamicTypes('double');
                                     if ($symbolVariable->isLocalOnly()) {
                                         $codePrinter->output('ZVAL_DOUBLE(&' . $variable . ', ' . $itemVariable->getName() . ');');
                                     } else {
                                         $codePrinter->output('ZVAL_DOUBLE(' . $variable . ', ' . $itemVariable->getName() . ');');
                                     }
                                     break;
                                 default:
                                     throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $itemVariable->getType(), $statement);
                             }
                             break;
                         case 'bool':
                             switch ($statement['operator']) {
                                 case 'assign':
                                     $symbolVariable->initVariant($compilationContext);
                                     $symbolVariable->setDynamicTypes('bool');
                                     if ($symbolVariable->isLocalOnly()) {
                                         $codePrinter->output('ZVAL_BOOL(&' . $variable . ', ' . $itemVariable->getName() . ');');
                                     } else {
                                         $codePrinter->output('ZVAL_BOOL(' . $variable . ', ' . $itemVariable->getName() . ');');
                                     }
                                     break;
                                 default:
                                     throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $itemVariable->getType(), $statement);
                             }
                             break;
                         case 'array':
                             switch ($statement['operator']) {
                                 case 'assign':
                                     if ($variable != $resolvedExpr->getCode()) {
                                         $symbolVariable->setMustInitNull(true);
                                         $compilationContext->symbolTable->mustGrownStack(true);
                                         /* Inherit the dynamic type data from the assigned value */
                                         $symbolVariable->setDynamicTypes('array');
                                         $codePrinter->output('ZEPHIR_CPY_WRT(' . $variable . ', ' . $resolvedExpr->getCode() . ');');
                                     }
                                     break;
                                 default:
                                     throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $resolvedExpr->getType(), $resolvedExpr->getOriginal());
                             }
                             break;
                         case 'variable':
                             switch ($statement['operator']) {
                                 case 'assign':
                                     if ($itemVariable->getName() != $variable) {
                                         $symbolVariable->setMustInitNull(true);
                                         $compilationContext->symbolTable->mustGrownStack(true);
                                         /* Inherit the dynamic type data from the assigned value */
                                         $symbolVariable->setDynamicTypes($itemVariable->getDynamicTypes());
                                         $symbolVariable->setClassTypes($itemVariable->getClassTypes());
                                         $codePrinter->output('ZEPHIR_CPY_WRT(' . $variable . ', ' . $itemVariable->getName() . ');');
                                         if ($itemVariable->isTemporal()) {
                                             $itemVariable->setIdle(true);
                                         }
                                     }
                                     break;
                                 case 'concat-assign':
                                     $compilationContext->headersManager->add('kernel/operators');
                                     $codePrinter->output('zephir_concat_self(&' . $variable . ', ' . $itemVariable->getName() . ' TSRMLS_CC);');
                                     break;
                                 case 'add-assign':
                                     $compilationContext->symbolTable->mustGrownStack(true);
                                     $compilationContext->headersManager->add('kernel/operators');
                                     $codePrinter->output('ZEPHIR_ADD_ASSIGN(' . $variable . ', ' . $itemVariable->getName() . ');');
                                     break;
                                 case 'sub-assign':
                                     $compilationContext->symbolTable->mustGrownStack(true);
                                     $compilationContext->headersManager->add('kernel/operators');
                                     $codePrinter->output('ZEPHIR_SUB_ASSIGN(' . $variable . ', ' . $itemVariable->getName() . ');');
                                     break;
                                 default:
                                     throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $itemVariable->getType(), $statement);
                             }
                             break;
                         case 'string':
                             switch ($statement['operator']) {
                                 case 'assign':
                                     if ($itemVariable->getName() != $variable) {
                                         $symbolVariable->setMustInitNull(true);
                                         $compilationContext->symbolTable->mustGrownStack(true);
                                         /* Inherit the dynamic type data from the assigned value */
                                         $symbolVariable->setDynamicTypes($itemVariable->getDynamicTypes());
                                         $symbolVariable->setClassTypes($itemVariable->getClassTypes());
                                         $codePrinter->output('ZEPHIR_CPY_WRT(' . $variable . ', ' . $itemVariable->getName() . ');');
                                         if ($itemVariable->isTemporal()) {
                                             $itemVariable->setIdle(true);
                                         }
                                     }
                                     break;
                                 case 'concat-assign':
                                     $compilationContext->headersManager->add('kernel/operators');
                                     $codePrinter->output('zephir_concat_self(&' . $variable . ', ' . $itemVariable->getName() . ' TSRMLS_CC);');
                                     break;
                                 default:
                                     throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $itemVariable->getType(), $statement);
                             }
                             break;
                         default:
                             throw new CompilerException("Unknown type: " . $itemVariable->getType(), $resolvedExpr->getOriginal());
                     }
                     break;
                 default:
                     throw new CompilerException("Unknown type: " . $resolvedExpr->getType(), $resolvedExpr->getOriginal());
             }
             break;
         default:
             throw new CompilerException("Unknown type: " . $type, $statement);
     }
 }
Ejemplo n.º 6
0
 /**
  * @param CompilationContext $compilationContext
  * @throws CompilerException
  */
 public function compile(CompilationContext $compilationContext)
 {
     $readDetector = new ReadDetector();
     $statement = $this->_statement;
     foreach ($statement['assignments'] as $assignment) {
         $variable = $assignment['variable'];
         /**
          * Get the symbol from the symbol table if necessary
          */
         switch ($assignment['assign-type']) {
             case 'static-property':
             case 'static-property-append':
             case 'static-property-array-index':
             case 'static-property-array-index-append':
             case 'dynamic-variable-string':
                 $symbolVariable = null;
                 break;
             case 'array-index':
             case 'variable-append':
             case 'object-property':
             case 'array-index-append':
             case 'string-dynamic-object-property':
             case 'variable-dynamic-object-property':
                 $symbolVariable = $compilationContext->symbolTable->getVariableForUpdate($variable, $compilationContext, $assignment);
                 break;
             default:
                 $symbolVariable = $compilationContext->symbolTable->getVariableForWrite($variable, $compilationContext, $assignment);
                 break;
         }
         /**
          * Incr/Decr assignments don't require an expression
          */
         if (isset($assignment['expr'])) {
             $expr = new Expression($assignment['expr']);
             switch ($assignment['assign-type']) {
                 case 'variable':
                     if (!$readDetector->detect($variable, $assignment['expr'])) {
                         if (isset($assignment['operator'])) {
                             if ($assignment['operator'] == 'assign') {
                                 $expr->setExpectReturn(true, $symbolVariable);
                             }
                         } else {
                             $expr->setExpectReturn(true, $symbolVariable);
                         }
                     } else {
                         if (isset($assignment['operator'])) {
                             if ($assignment['operator'] == 'assign') {
                                 $expr->setExpectReturn(true);
                             }
                         } else {
                             $expr->setExpectReturn(true);
                         }
                     }
                     break;
             }
             switch ($assignment['expr']['type']) {
                 case 'property-access':
                 case 'array-access':
                 case 'type-hint':
                     $expr->setReadOnly(true);
                     break;
             }
             $resolvedExpr = $expr->compile($compilationContext);
             /**
              * Bad implemented operators could return values different than objects
              */
             if (!is_object($resolvedExpr)) {
                 throw new CompilerException("Resolved expression is not valid", $assignment['expr']);
             }
         }
         /**
          * There are four types of assignments
          */
         switch ($assignment['assign-type']) {
             case 'variable':
                 $let = new LetVariable();
                 $let->assign($variable, $symbolVariable, $resolvedExpr, $readDetector, $compilationContext, $assignment);
                 break;
             case 'variable-append':
                 $let = new LetVariableAppend();
                 $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment);
                 break;
             case 'object-property':
                 $let = new LetObjectProperty();
                 $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment);
                 break;
             case 'variable-dynamic-object-property':
                 $let = new LetObjectDynamicProperty();
                 $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment);
                 break;
             case 'string-dynamic-object-property':
                 $let = new LetObjectDynamicStringProperty();
                 $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment);
                 break;
             case 'static-property':
                 $let = new LetStaticProperty();
                 $let->assignStatic($variable, $assignment['property'], $resolvedExpr, $compilationContext, $assignment);
                 break;
             case 'static-property-append':
                 $let = new LetStaticPropertyAppend();
                 $let->assignStatic($variable, $assignment['property'], $resolvedExpr, $compilationContext, $assignment);
                 break;
             case 'static-property-array-index':
                 $let = new LetStaticPropertyArrayIndex();
                 $let->assignStatic($variable, $assignment['property'], $resolvedExpr, $compilationContext, $assignment);
                 break;
             case 'static-property-array-index-append':
                 $let = new LetStaticPropertyArrayIndexAppend();
                 $let->assignStatic($variable, $assignment['property'], $resolvedExpr, $compilationContext, $assignment);
                 break;
             case 'array-index':
                 $let = new LetArrayIndex();
                 $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment);
                 break;
             case 'array-index-append':
                 $let = new LetArrayIndexAppend();
                 $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment);
                 break;
             case 'object-property-append':
                 $let = new LetObjectPropertyAppend();
                 $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment);
                 break;
             case 'object-property-array-index':
                 $let = new LetObjectPropertyArrayIndex();
                 $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment);
                 break;
             case 'object-property-array-index-append':
                 $let = new LetObjectPropertyArrayIndexAppend();
                 $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment);
                 break;
             case 'incr':
                 $let = new LetIncr();
                 $let->assign($variable, $symbolVariable, $compilationContext, $assignment);
                 break;
             case 'decr':
                 $let = new LetDecr();
                 $let->assign($variable, $symbolVariable, $compilationContext, $assignment);
                 break;
             case 'object-property-incr':
                 $let = new LetObjectPropertyIncr();
                 $let->assign($variable, $assignment['property'], $symbolVariable, $compilationContext, $assignment);
                 break;
             case 'object-property-decr':
                 $let = new LetObjectPropertyDecr();
                 $let->assign($variable, $assignment['property'], $symbolVariable, $compilationContext, $assignment);
                 break;
             case 'dynamic-variable':
                 $let = new LetExportSymbol();
                 $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment);
                 break;
             case 'dynamic-variable-string':
                 $let = new LetExportSymbolString();
                 $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment);
                 break;
             default:
                 throw new CompilerException("Unknown assignment: " . $assignment['assign-type'], $assignment);
         }
     }
 }