Пример #1
0
 /**
  * Compiles the file
  *
  * @param Compiler $compiler
  * @param StringsManager $stringsManager
  */
 public function compile(Compiler $compiler, StringsManager $stringsManager)
 {
     if (!$this->_ir) {
         throw new CompilerException('IR related to compiled file is missing');
     }
     /**
      * External classes should not be compiled as part of the extension
      */
     if ($this->_external) {
         return;
     }
     /**
      * Compilation context stores common objects required by compilation entities
      */
     $compilationContext = new CompilationContext();
     /**
      * Set global compiler in the compilation context
      */
     $compilationContext->compiler = $compiler;
     /**
      * Set global config in the compilation context
      */
     $compilationContext->config = $this->_config;
     /**
      * Set global logger in the compilation context
      */
     $compilationContext->logger = $this->_logger;
     /**
      * Set global strings manager
      */
     $compilationContext->stringsManager = $stringsManager;
     $compilationContext->backend = $compiler->backend;
     /**
      * Headers manager
      */
     $headersManager = new HeadersManager();
     $compilationContext->headersManager = $headersManager;
     /**
      * Main code-printer for the file
      */
     $codePrinter = new CodePrinter();
     $compilationContext->codePrinter = $codePrinter;
     /**
      * Alias manager
      */
     $compilationContext->aliasManager = $this->_aliasManager;
     $codePrinter->outputBlankLine();
     $class = false;
     $interface = false;
     foreach ($this->_ir as $topStatement) {
         switch ($topStatement['type']) {
             case 'class':
                 if ($interface || $class) {
                     throw new CompilerException("More than one class defined in the same file", $topStatement);
                 }
                 $class = true;
                 $this->compileClass($compilationContext, $this->_namespace, $topStatement);
                 break;
             case 'interface':
                 if ($interface || $class) {
                     throw new CompilerException("More than one class defined in the same file", $topStatement);
                 }
                 $class = true;
                 $this->compileClass($compilationContext, $this->_namespace, $topStatement);
                 break;
             case 'comment':
                 $this->compileComment($compilationContext, $topStatement);
                 break;
         }
     }
     /* ensure functions are handled last */
     foreach ($this->_functionDefinitions as $funcDef) {
         $this->compileFunction($compilationContext, $funcDef);
     }
     /* apply headers */
     $this->applyClassHeaders($compilationContext);
     $classDefinition = $this->_classDefinition;
     if (!$classDefinition) {
         $this->_ir = null;
         return;
     }
     $classDefinition->setOriginalNode($this->_originalNode);
     $completeName = $classDefinition->getCompleteName();
     $path = str_replace('\\', DIRECTORY_SEPARATOR, strtolower($completeName));
     $filePath = 'ext/' . $path . '.zep.c';
     $filePathHeader = 'ext/' . $path . '.zep.h';
     if (strpos($path, DIRECTORY_SEPARATOR)) {
         $dirname = dirname($filePath);
         if (!is_dir($dirname)) {
             mkdir($dirname, 0755, true);
         }
     }
     if ($codePrinter) {
         /**
          * If the file does not exists we create it for the first time
          */
         if (!file_exists($filePath)) {
             file_put_contents($filePath, $codePrinter->getOutput());
             if ($compilationContext->headerPrinter) {
                 file_put_contents($filePathHeader, $compilationContext->headerPrinter->getOutput());
             }
         } else {
             $fileSystem = $compiler->getFileSystem();
             /**
              * Use md5 hash to avoid rewrite the file again and again when it hasn't changed
              * thus avoiding unnecessary recompilations
              */
             $output = $codePrinter->getOutput();
             $hash = $fileSystem->getHashFile('md5', $filePath, true);
             if (md5($output) != $hash) {
                 file_put_contents($filePath, $output);
             }
             if ($compilationContext->headerPrinter) {
                 $output = $compilationContext->headerPrinter->getOutput();
                 $hash = $fileSystem->getHashFile('md5', $filePathHeader, true);
                 if (md5($output) != $hash) {
                     file_put_contents($filePathHeader, $output);
                 }
             }
         }
     }
     /**
      * Add to file compiled
      */
     $this->_compiledFile = $path . '.c';
     $this->_ir = null;
 }
Пример #2
0
 /**
  * Compiles the method
  *
  * @param CompilationContext $compilationContext
  * @return null
  * @throws CompilerException
  */
 public function compile(CompilationContext $compilationContext)
 {
     /**
      * Set the method currently being compiled
      */
     $compilationContext->currentMethod = $this;
     /**
      * Initialize the method warm-up to null
      */
     $compilationContext->methodWarmUp = null;
     /**
      * Assign pre-made compilation passses
      */
     $localContext = $this->localContext;
     $typeInference = $this->typeInference;
     $callGathererPass = $this->callGathererPass;
     /**
      * Every method has its own symbol table
      */
     $symbolTable = new SymbolTable($compilationContext);
     if ($localContext) {
         $symbolTable->setLocalContext($localContext);
     }
     /**
      * Parameters has an additional extra mutation
      */
     $parameters = $this->parameters;
     if ($localContext) {
         if (is_object($parameters)) {
             foreach ($parameters->getParameters() as $parameter) {
                 $localContext->increaseMutations($parameter['name']);
             }
         }
     }
     /**
      * Initialization of parameters happens in a fictitious external branch
      */
     $branch = new Branch();
     $branch->setType(Branch::TYPE_EXTERNAL);
     /**
      * BranchManager helps to create graphs of conditional/loop/root/jump branches
      */
     $branchManager = new BranchManager();
     $branchManager->addBranch($branch);
     /**
      * Cache Manager manages function calls, method calls and class entries caches
      */
     $cacheManager = new CacheManager();
     $cacheManager->setGatherer($callGathererPass);
     $compilationContext->branchManager = $branchManager;
     $compilationContext->cacheManager = $cacheManager;
     $compilationContext->typeInference = $typeInference;
     $compilationContext->symbolTable = $symbolTable;
     $oldCodePrinter = $compilationContext->codePrinter;
     /**
      * Change the code printer to a single method instance
      */
     $codePrinter = new CodePrinter();
     $compilationContext->codePrinter = $codePrinter;
     /**
      * Set an empty function cache
      */
     $compilationContext->functionCache = null;
     /**
      * Reset try/catch and loop counter
      */
     $compilationContext->insideCycle = 0;
     $compilationContext->insideTryCatch = 0;
     $compilationContext->currentTryCatch = 0;
     if (is_object($parameters)) {
         /**
          * Round 1. Create variables in parameters in the symbol table
          */
         $classCastChecks = array();
         foreach ($parameters->getParameters() as $parameter) {
             /**
              * Change dynamic variables to low level types
              */
             if ($typeInference) {
                 if (isset($parameter['data-type'])) {
                     if ($parameter['data-type'] == 'variable') {
                         $type = $typeInference->getInferedType($parameter['name']);
                         if (is_string($type)) {
                             /* promote polymorphic parameters to low level types */
                         }
                     }
                 } else {
                     $type = $typeInference->getInferedType($parameter['name']);
                     if (is_string($type)) {
                         /* promote polymorphic parameters to low level types */
                     }
                 }
             }
             $symbolParam = null;
             if (isset($parameter['data-type'])) {
                 switch ($parameter['data-type']) {
                     case 'object':
                     case 'callable':
                     case 'resource':
                     case 'variable':
                         $symbol = $symbolTable->addVariable($parameter['data-type'], $parameter['name'], $compilationContext);
                         break;
                     default:
                         $symbol = $symbolTable->addVariable($parameter['data-type'], $parameter['name'], $compilationContext);
                         $symbolParam = $symbolTable->addVariable('variable', $parameter['name'] . '_param', $compilationContext);
                         if ($parameter['data-type'] == 'string' || $parameter['data-type'] == 'array') {
                             $symbol->setMustInitNull(true);
                         }
                         break;
                 }
             } else {
                 $symbol = $symbolTable->addVariable('variable', $parameter['name'], $compilationContext);
             }
             /**
              * Some parameters can be read-only
              */
             if (isset($parameter['const']) && $parameter['const']) {
                 $symbol->setReadOnly(true);
                 if (is_object($symbolParam)) {
                     $symbolParam->setReadOnly(true);
                 }
             }
             if (is_object($symbolParam)) {
                 /**
                  * Parameters are marked as 'external'
                  */
                 $symbolParam->setIsExternal(true);
                 /**
                  * Assuming they're initialized
                  */
                 $symbolParam->setIsInitialized(true, $compilationContext, $parameter);
                 /**
                  * Initialize auxiliar parameter zvals to null
                  */
                 $symbolParam->setMustInitNull(true);
                 /**
                  * Increase uses
                  */
                 $symbolParam->increaseUses();
             } else {
                 if (isset($parameter['default'])) {
                     if (isset($parameter['data-type'])) {
                         if ($parameter['data-type'] == 'variable') {
                             $symbol->setMustInitNull(true);
                         }
                     } else {
                         $symbol->setMustInitNull(true);
                     }
                 }
             }
             /**
              * Original node where the variable was declared
              */
             $symbol->setOriginal($parameter);
             /**
              * Parameters are marked as 'external'
              */
             $symbol->setIsExternal(true);
             /**
              * Assuming they're initialized
              */
             $symbol->setIsInitialized(true, $compilationContext, $parameter);
             /**
              * Variables with class/type must be objects across the execution
              */
             if (isset($parameter['cast'])) {
                 $symbol->setDynamicTypes('object');
                 $symbol->setClassTypes($compilationContext->getFullName($parameter['cast']['value']));
                 $classCastChecks[] = array($symbol, $parameter);
             } else {
                 if (isset($parameter['data-type'])) {
                     if ($parameter['data-type'] == 'variable') {
                         $symbol->setDynamicTypes('undefined');
                     }
                 } else {
                     $symbol->setDynamicTypes('undefined');
                 }
             }
         }
     }
     /**
      * Initialize the properties within create_object, handler code
      */
     if ($this->getName() == 'zephir_init_properties') {
         $codePrinter->increaseLevel();
         $codePrinter->output('{');
         $codePrinter->increaseLevel();
         $codePrinter->output('zval *this_ptr = NULL;');
         $codePrinter->output('ZEPHIR_CREATE_OBJECT(this_ptr, class_type);');
         $codePrinter->decreaseLevel();
     }
     /**
      * Compile the block of statements if any
      */
     if (is_object($this->statements)) {
         if ($this->hasModifier('static')) {
             $compilationContext->staticContext = true;
         } else {
             $compilationContext->staticContext = false;
         }
         /**
          * Compile the statements block as a 'root' branch
          */
         $this->statements->compile($compilationContext, false, Branch::TYPE_ROOT);
     }
     /**
      * Initialize default values in dynamic variables
      */
     $initVarCode = "";
     foreach ($symbolTable->getVariables() as $variable) {
         /**
          * Initialize 'dynamic' variables with default values
          */
         if ($variable->getType() == 'variable') {
             if ($variable->getNumberUses() > 0) {
                 if ($variable->getName() != 'this_ptr' && $variable->getName() != 'return_value' && $variable->getName() != 'return_value_ptr') {
                     $defaultValue = $variable->getDefaultInitValue();
                     if (is_array($defaultValue)) {
                         $symbolTable->mustGrownStack(true);
                         switch ($defaultValue['type']) {
                             case 'int':
                             case 'uint':
                             case 'long':
                                 $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                                 $initVarCode .= "\t" . 'ZVAL_LONG(' . $variable->getName() . ', ' . $defaultValue['value'] . ');' . PHP_EOL;
                                 break;
                             case 'char':
                             case 'uchar':
                                 if (strlen($defaultValue['value']) > 2) {
                                     if (strlen($defaultValue['value']) > 10) {
                                         throw new CompilerException("Invalid char literal: '" . substr($defaultValue['value'], 0, 10) . "...'", $defaultValue);
                                     } else {
                                         throw new CompilerException("Invalid char literal: '" . $defaultValue['value'] . "'", $defaultValue);
                                     }
                                 }
                                 $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                                 $initVarCode .= "\t" . 'ZVAL_LONG(' . $variable->getName() . ', \'' . $defaultValue['value'] . '\');' . PHP_EOL;
                                 break;
                             case 'null':
                                 $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                                 $initVarCode .= "\t" . 'ZVAL_NULL(' . $variable->getName() . ');' . PHP_EOL;
                                 break;
                             case 'double':
                                 $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                                 $initVarCode .= "\t" . 'ZVAL_DOUBLE(' . $variable->getName() . ', ' . $defaultValue['value'] . ');' . PHP_EOL;
                                 break;
                             case 'string':
                                 $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                                 $initVarCode .= "\t" . 'ZVAL_STRING(' . $variable->getName() . ', "' . Utils::addSlashes($defaultValue['value'], true) . '", 1);' . PHP_EOL;
                                 break;
                             case 'array':
                             case 'empty-array':
                                 $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                                 $initVarCode .= "\t" . 'array_init(' . $variable->getName() . ');' . PHP_EOL;
                                 break;
                             default:
                                 throw new CompilerException('Invalid default type: ' . $defaultValue['type'] . ' for data type: ' . $variable->getType(), $variable->getOriginal());
                         }
                     }
                 }
             }
             continue;
         }
         /**
          * Initialize 'string' variables with default values
          */
         if ($variable->getType() == 'string') {
             if ($variable->getNumberUses() > 0) {
                 $defaultValue = $variable->getDefaultInitValue();
                 if (is_array($defaultValue)) {
                     $symbolTable->mustGrownStack(true);
                     switch ($defaultValue['type']) {
                         case 'string':
                             $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                             $initVarCode .= "\t" . 'ZVAL_STRING(' . $variable->getName() . ', "' . Utils::addSlashes($defaultValue['value'], true) . '", 1);' . PHP_EOL;
                             break;
                         case 'null':
                             $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                             $initVarCode .= "\t" . 'ZVAL_EMPTY_STRING(' . $variable->getName() . ');' . PHP_EOL;
                             break;
                         default:
                             throw new CompilerException('Invalid default type: ' . $defaultValue['type'] . ' for data type: ' . $variable->getType(), $variable->getOriginal());
                     }
                 }
             }
             continue;
         }
         /**
          * Initialize 'array' variables with default values
          */
         if ($variable->getType() == 'array') {
             if ($variable->getNumberUses() > 0) {
                 $defaultValue = $variable->getDefaultInitValue();
                 if (is_array($defaultValue)) {
                     $symbolTable->mustGrownStack(true);
                     switch ($defaultValue['type']) {
                         case 'null':
                             $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                             $initVarCode .= "\t" . 'ZVAL_NULL(' . $variable->getName() . ');' . PHP_EOL;
                             break;
                         case 'array':
                         case 'empty-array':
                             $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                             $initVarCode .= "\t" . 'array_init(' . $variable->getName() . ');' . PHP_EOL;
                             break;
                         default:
                             throw new CompilerException('Invalid default type: ' . $defaultValue['type'] . ' for data type: ' . $variable->getType(), $variable->getOriginal());
                     }
                 }
             }
         }
     }
     /**
      * Fetch parameters from vm-top
      */
     $initCode = "";
     $code = "";
     if (is_object($parameters)) {
         /**
          * Round 2. Fetch the parameters in the method
          */
         $params = array();
         $requiredParams = array();
         $optionalParams = array();
         $numberRequiredParams = 0;
         $numberOptionalParams = 0;
         foreach ($parameters->getParameters() as $parameter) {
             if (isset($parameter['data-type'])) {
                 $dataType = $parameter['data-type'];
             } else {
                 $dataType = 'variable';
             }
             switch ($dataType) {
                 case 'object':
                 case 'callable':
                 case 'resource':
                 case 'variable':
                     if (!$this->isInternal()) {
                         $params[] = '&' . $parameter['name'];
                     } else {
                         $params[] = $parameter['name'];
                     }
                     break;
                 default:
                     if (!$this->isInternal()) {
                         $params[] = '&' . $parameter['name'] . '_param';
                     } else {
                         $params[] = $parameter['name'] . '_param';
                     }
                     break;
             }
             if (isset($parameter['default'])) {
                 $optionalParams[] = $parameter;
                 $numberOptionalParams++;
             } else {
                 $requiredParams[] = $parameter;
                 $numberRequiredParams++;
             }
         }
         /**
          * Pass the write detector to the method statement block to check if the parameter
          * variable is modified so as do the proper separation
          */
         $parametersToSeparate = array();
         if (is_object($this->statements)) {
             /**
              * If local context is not available
              */
             if (!$localContext) {
                 $writeDetector = new WriteDetector();
             }
             foreach ($parameters->getParameters() as $parameter) {
                 if (isset($parameter['data-type'])) {
                     $dataType = $parameter['data-type'];
                 } else {
                     $dataType = 'variable';
                 }
                 switch ($dataType) {
                     case 'variable':
                     case 'string':
                     case 'array':
                     case 'resource':
                     case 'object':
                     case 'callable':
                         $name = $parameter['name'];
                         if (!$localContext) {
                             if ($writeDetector->detect($name, $this->statements->getStatements())) {
                                 $parametersToSeparate[$name] = true;
                             }
                         } else {
                             if ($localContext->getNumberOfMutations($name) > 1) {
                                 $parametersToSeparate[$name] = true;
                             }
                         }
                         break;
                 }
             }
         }
         /**
          * Initialize required parameters
          */
         foreach ($requiredParams as $parameter) {
             if (isset($parameter['mandatory'])) {
                 $mandatory = $parameter['mandatory'];
             } else {
                 $mandatory = 0;
             }
             if (isset($parameter['data-type'])) {
                 $dataType = $parameter['data-type'];
             } else {
                 $dataType = 'variable';
             }
             if ($dataType != 'variable') {
                 /**
                  * Assign value from zval to low level type
                  */
                 if ($mandatory) {
                     $initCode .= $this->checkStrictType($parameter, $compilationContext);
                 } else {
                     $initCode .= $this->assignZvalValue($parameter, $compilationContext);
                 }
             }
             switch ($dataType) {
                 case 'variable':
                 case 'resource':
                 case 'object':
                 case 'callable':
                     if (isset($parametersToSeparate[$parameter['name']])) {
                         $symbolTable->mustGrownStack(true);
                         $initCode .= "\t" . "ZEPHIR_SEPARATE_PARAM(" . $parameter['name'] . ");" . PHP_EOL;
                     }
                     break;
             }
         }
         /**
          * Initialize optional parameters
          */
         foreach ($optionalParams as $parameter) {
             if (isset($parameter['mandatory'])) {
                 $mandatory = $parameter['mandatory'];
             } else {
                 $mandatory = 0;
             }
             if (isset($parameter['data-type'])) {
                 $dataType = $parameter['data-type'];
             } else {
                 $dataType = 'variable';
             }
             switch ($dataType) {
                 case 'object':
                 case 'callable':
                 case 'resource':
                 case 'variable':
                     $name = $parameter['name'];
                     break;
                 default:
                     $name = $parameter['name'] . '_param';
                     break;
             }
             /**
              * Assign the default value according to the variable's type
              */
             $initCode .= "\t" . 'if (!' . $name . ') {' . PHP_EOL;
             $initCode .= $this->assignDefaultValue($parameter, $compilationContext);
             if (isset($parametersToSeparate[$name]) || $dataType != 'variable') {
                 $initCode .= "\t" . '} else {' . PHP_EOL;
                 if (isset($parametersToSeparate[$name])) {
                     $initCode .= "\t\t" . "ZEPHIR_SEPARATE_PARAM(" . $name . ");" . PHP_EOL;
                 } else {
                     if ($mandatory) {
                         $initCode .= $this->checkStrictType($parameter, $compilationContext, $mandatory);
                     } else {
                         $initCode .= "\t" . $this->assignZvalValue($parameter, $compilationContext);
                     }
                 }
             }
             $initCode .= "\t" . '}' . PHP_EOL;
         }
         /**
          * Fetch the parameters to zval pointers
          */
         $codePrinter->preOutputBlankLine();
         if (!$this->isInternal()) {
             $compilationContext->headersManager->add('kernel/memory');
             if ($symbolTable->getMustGrownStack()) {
                 $code .= "\t" . 'zephir_fetch_params(1, ' . $numberRequiredParams . ', ' . $numberOptionalParams . ', ' . join(', ', $params) . ');' . PHP_EOL;
             } else {
                 $code .= "\t" . 'zephir_fetch_params(0, ' . $numberRequiredParams . ', ' . $numberOptionalParams . ', ' . join(', ', $params) . ');' . PHP_EOL;
             }
         } else {
             foreach ($params as $param) {
                 $code .= "\t" . $param . ' = ' . $param . '_ext;' . PHP_EOL;
             }
         }
         $code .= PHP_EOL;
     }
     $code .= $initCode . $initVarCode;
     $codePrinter->preOutput($code);
     /**
      * Fetch used superglobals
      */
     foreach ($symbolTable->getVariables() as $name => $variable) {
         if ($symbolTable->isSuperGlobal($name)) {
             $codePrinter->preOutput("\t" . 'zephir_get_global(&' . $name . ', SS("' . $name . '") TSRMLS_CC);');
         }
     }
     /**
      * Grow the stack if needed
      */
     if ($symbolTable->getMustGrownStack()) {
         $compilationContext->headersManager->add('kernel/memory');
         $codePrinter->preOutput("\t" . 'ZEPHIR_MM_GROW();');
     }
     /**
      * Check if there are unused variables
      */
     $usedVariables = array();
     $completeName = $compilationContext->classDefinition->getCompleteName();
     foreach ($symbolTable->getVariables() as $variable) {
         if ($variable->getNumberUses() <= 0) {
             if ($variable->isExternal() == false) {
                 $compilationContext->logger->warning('Variable "' . $variable->getName() . '" declared but not used in ' . $completeName . '::' . $this->getName(), "unused-variable", $variable->getOriginal());
                 continue;
             }
             $compilationContext->logger->warning('Variable "' . $variable->getName() . '" declared but not used in ' . $completeName . '::' . $this->getName(), "unused-variable-external", $variable->getOriginal());
         }
         if ($variable->getName() != 'this_ptr' && $variable->getName() != 'return_value' && $variable->getName() != 'return_value_ptr') {
             $type = $variable->getType();
             if (!isset($usedVariables[$type])) {
                 $usedVariables[$type] = array();
             }
             $usedVariables[$type][] = $variable;
         }
     }
     /**
      * Check if there are assigned but not used variables
      * Warn whenever a variable is unused aside from its declaration.
      */
     foreach ($symbolTable->getVariables() as $variable) {
         if ($variable->isExternal() == true || $variable->isTemporal()) {
             continue;
         }
         if ($variable->getName() == 'this_ptr' || $variable->getName() == 'return_value' || $variable->getName() == 'return_value_ptr' || $variable->getName() == 'ZEPHIR_LAST_CALL_STATUS') {
             continue;
         }
         if (!$variable->isUsed()) {
             $node = $variable->getLastUsedNode();
             if (is_array($node)) {
                 $compilationContext->logger->warning('Variable "' . $variable->getName() . '" assigned but not used in ' . $completeName . '::' . $this->getName(), "unused-variable", $node);
             } else {
                 $compilationContext->logger->warning('Variable "' . $variable->getName() . '" assigned but not used in ' . $completeName . '::' . $this->getName(), "unused-variable", $variable->getOriginal());
             }
         }
     }
     if (count($usedVariables)) {
         $codePrinter->preOutputBlankLine();
     }
     /**
      * Generate the variable definition for variables used
      */
     foreach ($usedVariables as $type => $variables) {
         $pointer = null;
         switch ($type) {
             case 'int':
                 $code = 'int ';
                 break;
             case 'uint':
                 $code = 'unsigned int ';
                 break;
             case 'char':
                 $code = 'char ';
                 break;
             case 'uchar':
                 $code = 'unsigned char ';
                 break;
             case 'long':
                 $code = 'long ';
                 break;
             case 'ulong':
                 $code = 'unsigned long ';
                 break;
             case 'bool':
                 $code = 'zend_bool ';
                 break;
             case 'double':
                 $code = 'double ';
                 break;
             case 'string':
             case 'variable':
             case 'array':
             case 'null':
                 $pointer = '*';
                 $code = 'zval ';
                 break;
             case 'HashTable':
                 $pointer = '*';
                 $code = 'HashTable ';
                 break;
             case 'HashPosition':
                 $code = 'HashPosition ';
                 break;
             case 'zend_class_entry':
                 $pointer = '*';
                 $code = 'zend_class_entry ';
                 break;
             case 'zend_function':
                 $pointer = '*';
                 $code = 'zend_function ';
                 break;
             case 'zend_object_iterator':
                 $pointer = '*';
                 $code = 'zend_object_iterator ';
                 break;
             case 'zend_property_info':
                 $pointer = '*';
                 $code = 'zend_property_info ';
                 break;
             case 'zephir_fcall_cache_entry':
                 $pointer = '*';
                 $code = 'zephir_fcall_cache_entry ';
                 break;
             case 'static_zephir_fcall_cache_entry':
                 $pointer = '*';
                 $code = 'zephir_nts_static zephir_fcall_cache_entry ';
                 break;
             case 'static_zend_class_entry':
                 $pointer = '*';
                 $code = 'zephir_nts_static zend_class_entry ';
                 break;
             case 'zephir_ce_guard':
                 $code = 'zephir_nts_static zend_bool ';
                 break;
             default:
                 throw new CompilerException("Unsupported type in declare: " . $type);
         }
         $groupVariables = array();
         $defaultValues = array();
         /**
          * @var $variables Variable[]
          */
         foreach ($variables as $variable) {
             $isComplex = $type == 'variable' || $type == 'string' || $type == 'array' || $type == 'resource' || $type == 'callable' || $type == 'object';
             if ($isComplex && $variable->mustInitNull()) {
                 if ($variable->isLocalOnly()) {
                     $groupVariables[] = $variable->getName() . ' = zval_used_for_init';
                 } else {
                     if ($variable->isDoublePointer()) {
                         $groupVariables[] = $pointer . $pointer . $variable->getName() . ' = NULL';
                     } else {
                         $groupVariables[] = $pointer . $variable->getName() . ' = NULL';
                     }
                 }
                 continue;
             }
             if ($variable->isLocalOnly()) {
                 $groupVariables[] = $variable->getName();
                 continue;
             }
             if ($variable->isDoublePointer()) {
                 if ($variable->mustInitNull()) {
                     $groupVariables[] = $pointer . $pointer . $variable->getName() . ' = NULL';
                 } else {
                     $groupVariables[] = $pointer . $pointer . $variable->getName();
                 }
                 continue;
             }
             $defaultValue = $variable->getDefaultInitValue();
             if ($defaultValue !== null) {
                 switch ($type) {
                     case 'variable':
                     case 'string':
                     case 'array':
                     case 'resource':
                     case 'callable':
                     case 'object':
                         $groupVariables[] = $pointer . $variable->getName();
                         break;
                     case 'char':
                         if (strlen($defaultValue) > 4) {
                             if (strlen($defaultValue) > 10) {
                                 throw new CompilerException("Invalid char literal: '" . substr($defaultValue, 0, 10) . "...'", $variable->getOriginal());
                             } else {
                                 throw new CompilerException("Invalid char literal: '" . $defaultValue . "'", $variable->getOriginal());
                             }
                         }
                         /* no break */
                     /* no break */
                     default:
                         $groupVariables[] = $pointer . $variable->getName() . ' = ' . $defaultValue;
                         break;
                 }
                 continue;
             }
             if ($variable->mustInitNull() && $pointer) {
                 $groupVariables[] = $pointer . $variable->getName() . ' = NULL';
                 continue;
             }
             $groupVariables[] = $pointer . $variable->getName();
         }
         $codePrinter->preOutput("\t" . $code . join(', ', $groupVariables) . ';');
     }
     /**
      * Finalize the method compilation
      */
     if (is_object($this->statements)) {
         /**
          * If the last statement is not a 'return' or 'throw' we need to
          * restore the memory stack if needed
          */
         $lastType = $this->statements->getLastStatementType();
         if ($lastType != 'return' && $lastType != 'throw' && !$this->hasChildReturnStatementType($this->statements->getLastStatement())) {
             if ($symbolTable->getMustGrownStack()) {
                 $compilationContext->headersManager->add('kernel/memory');
                 $codePrinter->output("\t" . 'ZEPHIR_MM_RESTORE();');
             }
             /**
              * If a method has return-type hints we need to ensure the last statement is a 'return' statement
              */
             if ($this->hasReturnTypes()) {
                 throw new CompilerException('Reached end of the method without returning a valid type specified in the return-type hints', $this->expression['return-type']);
             }
         }
     }
     if ($this->getName() == 'zephir_init_properties') {
         $codePrinter->increaseLevel();
         $codePrinter->output('return Z_OBJVAL_P(this_ptr);');
         $codePrinter->decreaseLevel();
         $codePrinter->output('}');
         $codePrinter->decreaseLevel();
     }
     /**
      * Remove macros that grow/restore the memory frame stack if it wasn't used
      */
     $code = $this->removeMemoryStackReferences($symbolTable, $codePrinter->getOutput());
     /**
      * Restore the compilation context
      */
     $oldCodePrinter->output($code);
     $compilationContext->codePrinter = $oldCodePrinter;
     $compilationContext->branchManager = null;
     $compilationContext->cacheManager = null;
     $compilationContext->typeInference = null;
     $codePrinter->clear();
     return null;
 }
Пример #3
0
 /**
  * Compiles the method
  *
  * @param CompilationContext $compilationContext
  * @return null
  * @throws CompilerException
  */
 public function compile(CompilationContext $compilationContext)
 {
     /**
      * Set the method currently being compiled
      */
     $compilationContext->currentMethod = $this;
     /**
      * Initialize the method warm-up to null
      */
     $compilationContext->methodWarmUp = null;
     /**
      * Assign pre-made compilation passses
      */
     $localContext = $this->localContext;
     $typeInference = $this->typeInference;
     $callGathererPass = $this->callGathererPass;
     /**
      * Every method has its own symbol table
      */
     $symbolTable = new SymbolTable($compilationContext);
     if ($localContext) {
         $symbolTable->setLocalContext($localContext);
     }
     /**
      * Parameters has an additional extra mutation
      */
     $parameters = $this->parameters;
     if ($localContext) {
         if (is_object($parameters)) {
             foreach ($parameters->getParameters() as $parameter) {
                 $localContext->increaseMutations($parameter['name']);
             }
         }
     }
     /**
      * Initialization of parameters happens in a fictitious external branch
      */
     $branch = new Branch();
     $branch->setType(Branch::TYPE_EXTERNAL);
     /**
      * BranchManager helps to create graphs of conditional/loop/root/jump branches
      */
     $branchManager = new BranchManager();
     $branchManager->addBranch($branch);
     /**
      * Cache Manager manages function calls, method calls and class entries caches
      */
     $cacheManager = new CacheManager();
     $cacheManager->setGatherer($callGathererPass);
     $compilationContext->branchManager = $branchManager;
     $compilationContext->cacheManager = $cacheManager;
     $compilationContext->typeInference = $typeInference;
     $compilationContext->symbolTable = $symbolTable;
     $oldCodePrinter = $compilationContext->codePrinter;
     /**
      * Change the code printer to a single method instance
      */
     $codePrinter = new CodePrinter();
     $compilationContext->codePrinter = $codePrinter;
     /**
      * Set an empty function cache
      */
     $compilationContext->functionCache = null;
     /**
      * Reset try/catch and loop counter
      */
     $compilationContext->insideCycle = 0;
     $compilationContext->insideTryCatch = 0;
     $compilationContext->currentTryCatch = 0;
     if (is_object($parameters)) {
         /**
          * Round 1. Create variables in parameters in the symbol table
          */
         $classCastChecks = array();
         $substituteVars = array();
         foreach ($parameters->getParameters() as $parameter) {
             /**
              * Change dynamic variables to low level types
              */
             if ($typeInference) {
                 if (isset($parameter['data-type'])) {
                     if ($parameter['data-type'] == 'variable') {
                         $type = $typeInference->getInferedType($parameter['name']);
                         if (is_string($type)) {
                             /* promote polymorphic parameters to low level types */
                         }
                     }
                 } else {
                     $type = $typeInference->getInferedType($parameter['name']);
                     if (is_string($type)) {
                         /* promote polymorphic parameters to low level types */
                     }
                 }
             }
             $symbolParam = null;
             if (isset($parameter['data-type'])) {
                 switch ($parameter['data-type']) {
                     case 'object':
                     case 'callable':
                     case 'resource':
                     case 'variable':
                         $symbol = $symbolTable->addVariable($parameter['data-type'], $parameter['name'], $compilationContext);
                         /* TODO: Move this to the respective backend, which requires refactoring how this works */
                         if ($compilationContext->backend->isZE3()) {
                             $symbol->setIsDoublePointer(true);
                             $substituteVars[$parameter['name']] = $symbolTable->addVariable('variable', $parameter['name'] . '_sub', $compilationContext);
                         }
                         break;
                     default:
                         $symbol = $symbolTable->addVariable($parameter['data-type'], $parameter['name'], $compilationContext);
                         $symbolParam = $symbolTable->addVariable('variable', $parameter['name'] . '_param', $compilationContext);
                         /* TODO: Move this to the respective backend, which requires refactoring how this works */
                         if ($compilationContext->backend->isZE3()) {
                             $symbolParam->setIsDoublePointer(true);
                         }
                         if ($parameter['data-type'] == 'string' || $parameter['data-type'] == 'array') {
                             $symbol->setMustInitNull(true);
                         }
                         break;
                 }
             } else {
                 $symbol = $symbolTable->addVariable('variable', $parameter['name'], $compilationContext);
             }
             /* ZE3 only */
             if (isset($substituteVars[$parameter['name']])) {
                 $substituteVar = $substituteVars[$parameter['name']];
                 $substituteVar->increaseUses();
             }
             /**
              * Some parameters can be read-only
              */
             if (isset($parameter['const']) && $parameter['const']) {
                 $symbol->setReadOnly(true);
                 if (is_object($symbolParam)) {
                     $symbolParam->setReadOnly(true);
                 }
             }
             if (is_object($symbolParam)) {
                 /**
                  * Parameters are marked as 'external'
                  */
                 $symbolParam->setIsExternal(true);
                 /**
                  * Assuming they're initialized
                  */
                 $symbolParam->setIsInitialized(true, $compilationContext, $parameter);
                 /**
                  * Initialize auxiliar parameter zvals to null
                  */
                 $symbolParam->setMustInitNull(true);
                 /**
                  * Increase uses
                  */
                 $symbolParam->increaseUses();
             } else {
                 if (isset($parameter['default'])) {
                     if (isset($parameter['data-type'])) {
                         if ($parameter['data-type'] == 'variable') {
                             $symbol->setMustInitNull(true);
                         }
                     } else {
                         $symbol->setMustInitNull(true);
                     }
                 }
             }
             /**
              * Original node where the variable was declared
              */
             $symbol->setOriginal($parameter);
             /**
              * Parameters are marked as 'external'
              */
             $symbol->setIsExternal(true);
             /**
              * Assuming they're initialized
              */
             $symbol->setIsInitialized(true, $compilationContext, $parameter);
             /**
              * Variables with class/type must be objects across the execution
              */
             if (isset($parameter['cast'])) {
                 $symbol->setDynamicTypes('object');
                 $symbol->setClassTypes($compilationContext->getFullName($parameter['cast']['value']));
                 $classCastChecks[] = array($symbol, $parameter);
             } else {
                 if (isset($parameter['data-type'])) {
                     if ($parameter['data-type'] == 'variable') {
                         $symbol->setDynamicTypes('undefined');
                     }
                 } else {
                     $symbol->setDynamicTypes('undefined');
                 }
             }
         }
     }
     $compilationContext->backend->onPreCompile($this, $compilationContext);
     /**
      * Compile the block of statements if any
      */
     if (is_object($this->statements)) {
         if ($this->hasModifier('static')) {
             $compilationContext->staticContext = true;
         } else {
             $compilationContext->staticContext = false;
         }
         /**
          * Compile the statements block as a 'root' branch
          */
         $this->statements->compile($compilationContext, false, Branch::TYPE_ROOT);
     }
     /**
      * Initialize variable default values
      */
     $initVarCode = $compilationContext->backend->initializeVariableDefaults($symbolTable->getVariables(), $compilationContext);
     /**
      * Fetch parameters from vm-top
      */
     $initCode = "";
     $code = "";
     if (is_object($parameters)) {
         /**
          * Round 2. Fetch the parameters in the method
          */
         $params = array();
         $requiredParams = array();
         $optionalParams = array();
         $numberRequiredParams = 0;
         $numberOptionalParams = 0;
         foreach ($parameters->getParameters() as $parameter) {
             if (isset($parameter['data-type'])) {
                 $dataType = $parameter['data-type'];
             } else {
                 $dataType = 'variable';
             }
             switch ($dataType) {
                 case 'object':
                 case 'callable':
                 case 'resource':
                 case 'variable':
                     if (!$this->isInternal()) {
                         $params[] = '&' . $parameter['name'];
                     } else {
                         $params[] = $parameter['name'];
                     }
                     break;
                 default:
                     if (!$this->isInternal()) {
                         $params[] = '&' . $parameter['name'] . '_param';
                     } else {
                         $params[] = $parameter['name'] . '_param';
                     }
                     break;
             }
             if (isset($parameter['default'])) {
                 $optionalParams[] = $parameter;
                 $numberOptionalParams++;
             } else {
                 $requiredParams[] = $parameter;
                 $numberRequiredParams++;
             }
         }
         /**
          * Pass the write detector to the method statement block to check if the parameter
          * variable is modified so as do the proper separation
          */
         $parametersToSeparate = array();
         if (is_object($this->statements)) {
             /**
              * If local context is not available
              */
             if (!$localContext) {
                 $writeDetector = new WriteDetector();
             }
             foreach ($parameters->getParameters() as $parameter) {
                 if (isset($parameter['data-type'])) {
                     $dataType = $parameter['data-type'];
                 } else {
                     $dataType = 'variable';
                 }
                 switch ($dataType) {
                     case 'variable':
                     case 'string':
                     case 'array':
                     case 'resource':
                     case 'object':
                     case 'callable':
                         $name = $parameter['name'];
                         if (!$localContext) {
                             if ($writeDetector->detect($name, $this->statements->getStatements())) {
                                 $parametersToSeparate[$name] = true;
                             }
                         } else {
                             if ($localContext->getNumberOfMutations($name) > 1) {
                                 $parametersToSeparate[$name] = true;
                             }
                         }
                         break;
                 }
             }
         }
         /**
          * Initialize required parameters
          */
         foreach ($requiredParams as $parameter) {
             if (isset($parameter['mandatory'])) {
                 $mandatory = $parameter['mandatory'];
             } else {
                 $mandatory = 0;
             }
             if (isset($parameter['data-type'])) {
                 $dataType = $parameter['data-type'];
             } else {
                 $dataType = 'variable';
             }
             if ($dataType != 'variable') {
                 /**
                  * Assign value from zval to low level type
                  */
                 if ($mandatory) {
                     $initCode .= $this->checkStrictType($parameter, $compilationContext);
                 } else {
                     $initCode .= $this->assignZvalValue($parameter, $compilationContext);
                 }
             }
             switch ($dataType) {
                 case 'variable':
                 case 'resource':
                 case 'object':
                 case 'callable':
                     if (isset($parametersToSeparate[$parameter['name']])) {
                         $symbolTable->mustGrownStack(true);
                         $initCode .= "\t" . "ZEPHIR_SEPARATE_PARAM(" . $parameter['name'] . ");" . PHP_EOL;
                     }
                     break;
             }
         }
         /**
          * Initialize optional parameters
          */
         foreach ($optionalParams as $parameter) {
             if (isset($parameter['mandatory'])) {
                 $mandatory = $parameter['mandatory'];
             } else {
                 $mandatory = 0;
             }
             if (isset($parameter['data-type'])) {
                 $dataType = $parameter['data-type'];
             } else {
                 $dataType = 'variable';
             }
             switch ($dataType) {
                 case 'object':
                 case 'callable':
                 case 'resource':
                 case 'variable':
                     $name = $parameter['name'];
                     break;
                 default:
                     $name = $parameter['name'] . '_param';
                     break;
             }
             /**
              * Assign the default value according to the variable's type
              */
             $targetVar = $compilationContext->symbolTable->getVariableForWrite($name, $compilationContext);
             $initCode .= "\t" . $compilationContext->backend->ifVariableValueUndefined($targetVar, $compilationContext, false, false) . PHP_EOL;
             if ($compilationContext->backend->isZE3()) {
                 if ($targetVar->isDoublePointer() && isset($substituteVars[$parameter['name']])) {
                     $substituteVar = $substituteVars[$parameter['name']];
                     $initCode .= "\t\t" . $targetVar->getName() . ' = &' . $substituteVar->getName() . ';' . PHP_EOL;
                 }
             }
             $initCode .= $this->assignDefaultValue($parameter, $compilationContext);
             if (isset($parametersToSeparate[$name]) || $dataType != 'variable') {
                 $initCode .= "\t" . '} else {' . PHP_EOL;
                 if (isset($parametersToSeparate[$name])) {
                     $initCode .= "\t\t" . "ZEPHIR_SEPARATE_PARAM(" . $name . ");" . PHP_EOL;
                 } else {
                     if ($mandatory) {
                         $initCode .= $this->checkStrictType($parameter, $compilationContext, $mandatory);
                     } else {
                         $initCode .= "\t" . $this->assignZvalValue($parameter, $compilationContext);
                     }
                 }
             }
             $initCode .= "\t" . '}' . PHP_EOL;
         }
         /**
          * Fetch the parameters to zval pointers
          */
         $codePrinter->preOutputBlankLine();
         if (!$this->isInternal()) {
             $compilationContext->headersManager->add('kernel/memory');
             if ($symbolTable->getMustGrownStack()) {
                 $code .= "\t" . 'zephir_fetch_params(1, ' . $numberRequiredParams . ', ' . $numberOptionalParams . ', ' . join(', ', $params) . ');' . PHP_EOL;
             } else {
                 $code .= "\t" . 'zephir_fetch_params(0, ' . $numberRequiredParams . ', ' . $numberOptionalParams . ', ' . join(', ', $params) . ');' . PHP_EOL;
             }
         } else {
             foreach ($params as $param) {
                 /* TODO: Migrate all this code to codeprinter, get rid of temp code printer */
                 $tempCodePrinter = new CodePrinter();
                 $realCodePrinter = $compilationContext->codePrinter;
                 $compilationContext->codePrinter = $tempCodePrinter;
                 $paramVar = $compilationContext->symbolTable->getVariableForRead($param, $compilationContext);
                 $compilationContext->backend->assignZval($paramVar, $param . '_ext', $compilationContext);
                 $code .= "\t" . $tempCodePrinter->getOutput() . PHP_EOL;
                 $compilationContext->codePrinter = $realCodePrinter;
             }
         }
         $code .= PHP_EOL;
     }
     $code .= $initCode . $initVarCode;
     $codePrinter->preOutput($code);
     /**
      * Fetch used superglobals
      */
     foreach ($symbolTable->getVariables() as $name => $variable) {
         if ($symbolTable->isSuperGlobal($name)) {
             $globalVar = $symbolTable->getVariable($name);
             $codePrinter->preOutput("\t" . $compilationContext->backend->fetchGlobal($globalVar, $compilationContext, false));
         }
     }
     /**
      * Grow the stack if needed
      */
     if ($symbolTable->getMustGrownStack()) {
         $compilationContext->headersManager->add('kernel/memory');
         $codePrinter->preOutput("\t" . 'ZEPHIR_MM_GROW();');
     }
     /**
      * Check if there are unused variables
      */
     $usedVariables = array();
     $classDefinition = $this->getClassDefinition();
     if ($classDefinition) {
         $completeName = $classDefinition->getCompleteName();
     } else {
         $completeName = '[unknown]';
     }
     foreach ($symbolTable->getVariables() as $variable) {
         if ($variable->getNumberUses() <= 0) {
             if ($variable->isExternal() == false) {
                 $compilationContext->logger->warning('Variable "' . $variable->getName() . '" declared but not used in ' . $completeName . '::' . $this->getName(), "unused-variable", $variable->getOriginal());
                 continue;
             }
             $compilationContext->logger->warning('Variable "' . $variable->getName() . '" declared but not used in ' . $completeName . '::' . $this->getName(), "unused-variable-external", $variable->getOriginal());
         }
         if ($variable->getName() != 'this_ptr' && $variable->getName() != 'return_value' && $variable->getName() != 'return_value_ptr') {
             $type = $variable->getType();
             if (!isset($usedVariables[$type])) {
                 $usedVariables[$type] = array();
             }
             $usedVariables[$type][] = $variable;
         }
     }
     /**
      * Check if there are assigned but not used variables
      * Warn whenever a variable is unused aside from its declaration.
      */
     foreach ($symbolTable->getVariables() as $variable) {
         if ($variable->isExternal() == true || $variable->isTemporal()) {
             continue;
         }
         if ($variable->getName() == 'this_ptr' || $variable->getName() == 'return_value' || $variable->getName() == 'return_value_ptr' || $variable->getName() == 'ZEPHIR_LAST_CALL_STATUS') {
             continue;
         }
         if (!$variable->isUsed()) {
             $node = $variable->getLastUsedNode();
             if (is_array($node)) {
                 $compilationContext->logger->warning('Variable "' . $variable->getName() . '" assigned but not used in ' . $completeName . '::' . $this->getName(), "unused-variable", $node);
             } else {
                 $compilationContext->logger->warning('Variable "' . $variable->getName() . '" assigned but not used in ' . $completeName . '::' . $this->getName(), "unused-variable", $variable->getOriginal());
             }
         }
     }
     if (count($usedVariables)) {
         $codePrinter->preOutputBlankLine();
     }
     /**
      * Generate the variable definition for variables used
      */
     $initCode = "\t" . implode(PHP_EOL . "\t", $compilationContext->backend->declareVariables($this, $usedVariables, $compilationContext));
     if ($initCode) {
         $codePrinter->preOutput($initCode);
     }
     /**
      * Finalize the method compilation
      */
     if (is_object($this->statements)) {
         /**
          * If the last statement is not a 'return' or 'throw' we need to
          * restore the memory stack if needed
          */
         $lastType = $this->statements->getLastStatementType();
         if ($lastType != 'return' && $lastType != 'throw' && !$this->hasChildReturnStatementType($this->statements->getLastStatement())) {
             if ($symbolTable->getMustGrownStack()) {
                 $compilationContext->headersManager->add('kernel/memory');
                 $codePrinter->output("\t" . 'ZEPHIR_MM_RESTORE();');
             }
             /**
              * If a method has return-type hints we need to ensure the last statement is a 'return' statement
              */
             if ($this->hasReturnTypes()) {
                 throw new CompilerException('Reached end of the method without returning a valid type specified in the return-type hints', $this->expression['return-type']);
             }
         }
     }
     $compilationContext->backend->onPostCompile($this, $compilationContext);
     /**
      * Remove macros that grow/restore the memory frame stack if it wasn't used
      */
     $code = $this->removeMemoryStackReferences($symbolTable, $codePrinter->getOutput());
     /**
      * Restore the compilation context
      */
     $oldCodePrinter->output($code);
     $compilationContext->codePrinter = $oldCodePrinter;
     $compilationContext->branchManager = null;
     $compilationContext->cacheManager = null;
     $compilationContext->typeInference = null;
     $codePrinter->clear();
     return null;
 }
Пример #4
0
 /**
  * Compiles a class/interface
  *
  * @param CompilationContext $compilationContext
  *
  * @throws CompilerException
  * @throws Exception
  */
 public function compile(CompilationContext $compilationContext)
 {
     /**
      * Sets the current object as global class definition
      */
     $compilationContext->classDefinition = $this;
     /**
      * Get the global codePrinter
      */
     $codePrinter = $compilationContext->codePrinter;
     /**
      * The ZEPHIR_INIT_CLASS defines properties and constants exported by the class
      */
     $initClassName = $this->getCNamespace() . '_' . $this->getName();
     $codePrinter->output('ZEPHIR_INIT_CLASS(' . $initClassName . ') {');
     $codePrinter->outputBlankLine();
     $codePrinter->increaseLevel();
     /**
      * Method entry
      */
     $methods =& $this->methods;
     $initMethod = $this->getLocalOrParentInitMethod();
     if (count($methods) || $initMethod) {
         $methodEntry = strtolower($this->getCNamespace()) . '_' . strtolower($this->getName()) . '_method_entry';
     } else {
         $methodEntry = 'NULL';
     }
     foreach ($methods as $method) {
         $method->setupOptimized($compilationContext);
     }
     $namespace = str_replace('\\', '_', $compilationContext->config->get('namespace'));
     $flags = '0';
     if ($this->isAbstract()) {
         $flags = 'ZEND_ACC_EXPLICIT_ABSTRACT_CLASS';
     }
     if ($this->isFinal()) {
         if ($flags == '0') {
             $flags = 'ZEND_ACC_FINAL_CLASS';
         } else {
             $flags .= '|ZEND_ACC_FINAL_CLASS';
         }
     }
     /**
      * Register the class with extends + interfaces
      */
     $classExtendsDefinition = null;
     if ($this->extendsClass) {
         $classExtendsDefinition = $this->extendsClassDefinition;
         if ($classExtendsDefinition instanceof ClassDefinition && !$classExtendsDefinition->isBundled()) {
             $classEntry = $classExtendsDefinition->getClassEntry($compilationContext);
         } else {
             $classEntry = $this->getClassEntryByClassName($classExtendsDefinition->getName(), $compilationContext);
         }
         if ($this->getType() == 'class') {
             $codePrinter->output('ZEPHIR_REGISTER_CLASS_EX(' . $this->getNCNamespace() . ', ' . $this->getName() . ', ' . $namespace . ', ' . strtolower($this->getSCName($namespace)) . ', ' . $classEntry . ', ' . $methodEntry . ', ' . $flags . ');');
             $codePrinter->outputBlankLine();
         } else {
             $codePrinter->output('ZEPHIR_REGISTER_INTERFACE_EX(' . $this->getNCNamespace() . ', ' . $this->getName() . ', ' . $namespace . ', ' . strtolower($this->getSCName($namespace)) . ', ' . $classEntry . ', ' . $methodEntry . ');');
             $codePrinter->outputBlankLine();
         }
     } else {
         if ($this->getType() == 'class') {
             $codePrinter->output('ZEPHIR_REGISTER_CLASS(' . $this->getNCNamespace() . ', ' . $this->getName() . ', ' . $namespace . ', ' . strtolower($this->getSCName($namespace)) . ', ' . $methodEntry . ', ' . $flags . ');');
         } else {
             $codePrinter->output('ZEPHIR_REGISTER_INTERFACE(' . $this->getNCNamespace() . ', ' . $this->getName() . ', ' . $namespace . ', ' . strtolower($this->getSCName($namespace)) . ', ' . $methodEntry . ');');
         }
         $codePrinter->outputBlankLine();
     }
     /**
      * Compile properties
      * @var $property ClassProperty
      */
     foreach ($this->getProperties() as $property) {
         $docBlock = $property->getDocBlock();
         if ($docBlock) {
             $codePrinter->outputDocBlock($docBlock, true);
         }
         $property->compile($compilationContext);
         $codePrinter->outputBlankLine();
     }
     $initMethod = $this->getInitMethod();
     if ($initMethod) {
         $codePrinter->output($namespace . '_' . strtolower($this->getSCName($namespace)) . '_ce->create_object = ' . $initMethod->getName() . ';');
     }
     /**
      * Compile constants
      * @var $constant ClassConstant
      */
     foreach ($this->getConstants() as $constant) {
         $docBlock = $constant->getDocBlock();
         if ($docBlock) {
             $codePrinter->outputDocBlock($docBlock, true);
         }
         $constant->compile($compilationContext);
         $codePrinter->outputBlankLine();
     }
     /**
      * Implemented interfaces
      */
     $interfaces = $this->interfaces;
     $compiler = $compilationContext->compiler;
     if (is_array($interfaces)) {
         $codePrinter->outputBlankLine(true);
         foreach ($interfaces as $interface) {
             /**
              * Try to find the interface
              */
             $classEntry = false;
             if ($compiler->isInterface($interface)) {
                 $classInterfaceDefinition = $compiler->getClassDefinition($interface);
                 $classEntry = $classInterfaceDefinition->getClassEntry($compilationContext);
             } else {
                 if ($compiler->isBundledInterface($interface)) {
                     $classInterfaceDefinition = $compiler->getInternalClassDefinition($interface);
                     $classEntry = $this->getClassEntryByClassName($classInterfaceDefinition->getName(), $compilationContext);
                 }
             }
             if (!$classEntry) {
                 if ($compiler->isClass($interface)) {
                     throw new CompilerException("Cannot locate interface " . $interface . " when implementing interfaces on " . $this->getCompleteName() . '. ' . $interface . ' is currently a class', $this->originalNode);
                 } else {
                     throw new CompilerException("Cannot locate interface " . $interface . " when implementing interfaces on " . $this->getCompleteName(), $this->originalNode);
                 }
             }
             /**
              * We don't check if abstract classes implement the methods in their interfaces
              */
             if (!$this->isAbstract() && !$this->isInterface()) {
                 $this->checkInterfaceImplements($this, $classInterfaceDefinition);
             }
             $codePrinter->output('zend_class_implements(' . $this->getClassEntry() . ' TSRMLS_CC, 1, ' . $classEntry . ');');
         }
     }
     if (!$this->isAbstract() && !$this->isInterface()) {
         /**
          * Interfaces in extended classes may have
          */
         if ($classExtendsDefinition) {
             if ($classExtendsDefinition instanceof ClassDefinition && !$classExtendsDefinition->isBundled()) {
                 $interfaces = $classExtendsDefinition->getImplementedInterfaces();
                 if (is_array($interfaces)) {
                     foreach ($interfaces as $interface) {
                         $classInterfaceDefinition = null;
                         if ($compiler->isInterface($interface)) {
                             $classInterfaceDefinition = $compiler->getClassDefinition($interface);
                         } else {
                             if ($compiler->isBundledInterface($interface)) {
                                 $classInterfaceDefinition = $compiler->getInternalClassDefinition($interface);
                             }
                         }
                         if ($classInterfaceDefinition) {
                             $this->checkInterfaceImplements($this, $classInterfaceDefinition);
                         }
                     }
                 }
             }
         }
     }
     $codePrinter->output('return SUCCESS;');
     $codePrinter->outputBlankLine();
     $codePrinter->decreaseLevel();
     $codePrinter->output('}');
     $codePrinter->outputBlankLine();
     /**
      * Compile methods
      */
     foreach ($methods as $method) {
         $docBlock = $method->getDocBlock();
         if ($docBlock) {
             $codePrinter->outputDocBlock($docBlock);
         }
         if ($this->getType() == 'class') {
             if (!$method->isInternal()) {
                 $codePrinter->output('PHP_METHOD(' . $this->getCNamespace() . '_' . $this->getName() . ', ' . $method->getName() . ') {');
             } else {
                 $codePrinter->output($compilationContext->backend->getInternalSignature($method, $compilationContext) . ' {');
             }
             $codePrinter->outputBlankLine();
             if (!$method->isAbstract()) {
                 $method->compile($compilationContext);
             }
             $codePrinter->output('}');
             $codePrinter->outputBlankLine();
         } else {
             $codePrinter->output('ZEPHIR_DOC_METHOD(' . $this->getCNamespace() . '_' . $this->getName() . ', ' . $method->getName() . ');');
             $codePrinter->outputBlankLine();
         }
     }
     /**
      * Check whether classes must be exported
      */
     $exportClasses = $compilationContext->config->get('export-classes', 'extra');
     if ($exportClasses) {
         $exportAPI = 'extern ZEPHIR_API';
     } else {
         $exportAPI = 'extern';
     }
     /**
      * Create a code printer for the header file
      */
     $codePrinter = new CodePrinter();
     $codePrinter->outputBlankLine();
     $codePrinter->output($exportAPI . ' zend_class_entry *' . $this->getClassEntry() . ';');
     $codePrinter->outputBlankLine();
     $codePrinter->output('ZEPHIR_INIT_CLASS(' . $this->getCNamespace() . '_' . $this->getName() . ');');
     $codePrinter->outputBlankLine();
     if ($this->getType() == 'class') {
         if (count($methods)) {
             foreach ($methods as $method) {
                 if (!$method->isInternal()) {
                     $codePrinter->output('PHP_METHOD(' . $this->getCNamespace() . '_' . $this->getName() . ', ' . $method->getName() . ');');
                 } else {
                     $internalSignature = $compilationContext->backend->getInternalSignature($method, $compilationContext);
                     $codePrinter->output($internalSignature . ';');
                 }
             }
             $codePrinter->outputBlankLine();
         }
     }
     /**
      * Create argument info
      */
     foreach ($methods as $method) {
         $parameters = $method->getParameters();
         if (count($parameters)) {
             $codePrinter->output('ZEND_BEGIN_ARG_INFO_EX(arginfo_' . strtolower($this->getCNamespace() . '_' . $this->getName() . '_' . $method->getName()) . ', 0, 0, ' . $method->getNumberOfRequiredParameters() . ')');
             foreach ($parameters->getParameters() as $parameter) {
                 switch ($parameter['data-type']) {
                     case 'array':
                         $codePrinter->output("\t" . 'ZEND_ARG_ARRAY_INFO(0, ' . $parameter['name'] . ', ' . (isset($parameter['default']) ? 1 : 0) . ')');
                         break;
                     case 'variable':
                         if (isset($parameter['cast'])) {
                             switch ($parameter['cast']['type']) {
                                 case 'variable':
                                     $value = $parameter['cast']['value'];
                                     $codePrinter->output("\t" . 'ZEND_ARG_OBJ_INFO(0, ' . $parameter['name'] . ', ' . Utils::escapeClassName($compilationContext->getFullName($value)) . ', ' . (isset($parameter['default']) ? 1 : 0) . ')');
                                     break;
                                 default:
                                     throw new Exception('Unexpected exception');
                             }
                         } else {
                             $codePrinter->output("\t" . 'ZEND_ARG_INFO(0, ' . $parameter['name'] . ')');
                         }
                         break;
                     default:
                         $codePrinter->output("\t" . 'ZEND_ARG_INFO(0, ' . $parameter['name'] . ')');
                         break;
                 }
             }
             $codePrinter->output('ZEND_END_ARG_INFO()');
             $codePrinter->outputBlankLine();
         }
     }
     if (count($methods)) {
         $codePrinter->output('ZEPHIR_INIT_FUNCS(' . strtolower($this->getCNamespace() . '_' . $this->getName()) . '_method_entry) {');
         foreach ($methods as $method) {
             $parameters = $method->getParameters();
             if ($this->getType() == 'class') {
                 if (!$method->isInternal()) {
                     if (count($parameters)) {
                         $codePrinter->output("\t" . 'PHP_ME(' . $this->getCNamespace() . '_' . $this->getName() . ', ' . $method->getName() . ', arginfo_' . strtolower($this->getCNamespace() . '_' . $this->getName() . '_' . $method->getName()) . ', ' . $method->getModifiers() . ')');
                     } else {
                         $codePrinter->output("\t" . 'PHP_ME(' . $this->getCNamespace() . '_' . $this->getName() . ', ' . $method->getName() . ', NULL, ' . $method->getModifiers() . ')');
                     }
                 }
             } else {
                 if ($method->isStatic()) {
                     if (count($parameters)) {
                         $codePrinter->output("\t" . 'ZEND_FENTRY(' . $method->getName() . ', NULL, arginfo_' . strtolower($this->getCNamespace() . '_' . $this->getName() . '_' . $method->getName()) . ', ZEND_ACC_STATIC|ZEND_ACC_ABSTRACT|ZEND_ACC_PUBLIC)');
                     } else {
                         $codePrinter->output("\t" . 'ZEND_FENTRY(' . $method->getName() . ', NULL, NULL, ZEND_ACC_STATIC|ZEND_ACC_ABSTRACT|ZEND_ACC_PUBLIC)');
                     }
                 } else {
                     if (count($parameters)) {
                         $codePrinter->output("\t" . 'PHP_ABSTRACT_ME(' . $this->getCNamespace() . '_' . $this->getName() . ', ' . $method->getName() . ', arginfo_' . strtolower($this->getCNamespace() . '_' . $this->getName() . '_' . $method->getName()) . ')');
                     } else {
                         $codePrinter->output("\t" . 'PHP_ABSTRACT_ME(' . $this->getCNamespace() . '_' . $this->getName() . ', ' . $method->getName() . ', NULL)');
                     }
                 }
             }
         }
         $codePrinter->output("\t" . 'PHP_FE_END');
         $codePrinter->output('};');
     }
     $compilationContext->headerPrinter = $codePrinter;
 }
Пример #5
0
 /**
  * Compiles the file
  *
  * @param Compiler $compiler
  * @param StringsManager $stringsManager
  */
 public function compile(Compiler $compiler, StringsManager $stringsManager)
 {
     /**
      * Compilation context stores common objects required by compilation entities
      */
     $compilationContext = new CompilationContext();
     /**
      * Set global compiler in the compilation context
      */
     $compilationContext->compiler = $compiler;
     /**
      * Set global config in the compilation context
      */
     $compilationContext->config = $this->config;
     /**
      * Set global logger in the compilation context
      */
     $compilationContext->logger = $this->logger;
     /**
      * Set global strings manager
      */
     $compilationContext->stringsManager = $stringsManager;
     $compilationContext->backend = $compiler->backend;
     /**
      * Headers manager
      */
     $headersManager = new HeadersManager();
     $compilationContext->headersManager = $headersManager;
     /**
      * Main code-printer for the file
      */
     $codePrinter = new CodePrinter();
     $compilationContext->codePrinter = $codePrinter;
     /**
      * Alias manager
      */
     $compilationContext->aliasManager = new AliasManager();
     $codePrinter->outputBlankLine();
     $classDefinition = $this->classDefinition;
     $this->compileClass($classDefinition, $compilationContext);
     $completeName = $classDefinition->getCompleteName();
     $path = str_replace('\\', DIRECTORY_SEPARATOR, strtolower($completeName));
     $filePath = 'ext/' . $path . '.zep.c';
     $filePathHeader = 'ext/' . $path . '.zep.h';
     if (strpos($path, DIRECTORY_SEPARATOR)) {
         $dirname = dirname($filePath);
         if (!is_dir($dirname)) {
             mkdir($dirname, 0755, true);
         }
     }
     if ($codePrinter) {
         /**
          * If the file does not exists we create it for the first time
          */
         if (!file_exists($filePath)) {
             file_put_contents($filePath, $codePrinter->getOutput());
             if ($compilationContext->headerPrinter) {
                 file_put_contents($filePathHeader, $compilationContext->headerPrinter->getOutput());
             }
         } else {
             /**
              * Use md5 hash to avoid rewrite the file again and again when it hasn't changed
              * thus avoiding unnecessary recompilations
              */
             $output = $codePrinter->getOutput();
             $hash = hash_file('md5', $filePath);
             if (md5($output) != $hash) {
                 file_put_contents($filePath, $output);
             }
             if ($compilationContext->headerPrinter) {
                 $output = $compilationContext->headerPrinter->getOutput();
                 $hash = hash_file('md5', $filePathHeader);
                 if (md5($output) != $hash) {
                     file_put_contents($filePathHeader, $output);
                 }
             }
         }
     }
     /**
      * Add to file compiled
      */
     $this->compiledFile = $path . '.c';
 }
Пример #6
0
 public function initializeVariableDefaults($variables, CompilationContext $compilationContext)
 {
     $codePrinter = new CodePrinter();
     $codePrinter->increaseLevel();
     $oldCodePrinter = $compilationContext->codePrinter;
     $compilationContext->codePrinter = $codePrinter;
     /* Initialize default values in dynamic variables */
     foreach ($variables as $variable) {
         /**
          * Initialize 'dynamic' variables with default values
          */
         if ($variable->getType() == 'variable') {
             if ($variable->getNumberUses() > 0) {
                 if ($variable->getName() != 'this_ptr' && $variable->getName() != 'return_value' && $variable->getName() != 'return_value_ptr') {
                     $defaultValue = $variable->getDefaultInitValue();
                     if (is_array($defaultValue)) {
                         $symbolTable->mustGrownStack(true);
                         $compilationContext->backend->initVar($variable, $compilationContext);
                         switch ($defaultValue['type']) {
                             case 'int':
                             case 'uint':
                             case 'long':
                                 $compilationContext->backend->assignLong($variable, $defaultValue['value'], $compilationContext);
                                 break;
                             case 'bool':
                                 $compilationContext->backend->assignBool($variable, $defaultValue['value'], $compilationContext);
                                 break;
                             case 'char':
                             case 'uchar':
                                 if (strlen($defaultValue['value']) > 2) {
                                     if (strlen($defaultValue['value']) > 10) {
                                         throw new CompilerException("Invalid char literal: '" . substr($defaultValue['value'], 0, 10) . "...'", $defaultValue);
                                     } else {
                                         throw new CompilerException("Invalid char literal: '" . $defaultValue['value'] . "'", $defaultValue);
                                     }
                                 }
                                 $compilationContext->backend->assignLong($variable, '\'' . $defaultValue['value'] . '\'', $compilationContext);
                                 break;
                             case 'null':
                                 $compilationContext->backend->assignNull($variable, $compilationContext);
                                 break;
                             case 'double':
                                 $compilationContext->backend->assignDouble($variable, $defaultValue['value'], $compilationContext);
                                 break;
                             case 'string':
                                 $compilationContext->backend->assignString($variable, Utils::addSlashes($defaultValue['value'], true), $compilationContext);
                                 break;
                             case 'array':
                             case 'empty-array':
                                 $compilationContext->backend->initArray($variable, $compilationContext, null);
                                 break;
                             default:
                                 throw new CompilerException('Invalid default type: ' . $defaultValue['type'] . ' for data type: ' . $variable->getType(), $variable->getOriginal());
                         }
                     }
                 }
             }
             continue;
         }
         /**
          * Initialize 'string' variables with default values
          */
         if ($variable->getType() == 'string') {
             if ($variable->getNumberUses() > 0) {
                 $defaultValue = $variable->getDefaultInitValue();
                 if (is_array($defaultValue)) {
                     $symbolTable->mustGrownStack(true);
                     $compilationContext->backend->initVar($variable, $compilationContext);
                     switch ($defaultValue['type']) {
                         case 'string':
                             $compilationContext->backend->assignString($variable, Utils::addSlashes($defaultValue['value'], true), $compilationContext);
                             break;
                         case 'null':
                             $compilationContext->backend->assignString($variable, null, $compilationContext);
                             break;
                         default:
                             throw new CompilerException('Invalid default type: ' . $defaultValue['type'] . ' for data type: ' . $variable->getType(), $variable->getOriginal());
                     }
                 }
             }
             continue;
         }
         /**
          * Initialize 'array' variables with default values
          */
         if ($variable->getType() == 'array') {
             if ($variable->getNumberUses() > 0) {
                 $defaultValue = $variable->getDefaultInitValue();
                 if (is_array($defaultValue)) {
                     $symbolTable->mustGrownStack(true);
                     $compilationContext->backend->initVar($variable, $compilationContext);
                     switch ($defaultValue['type']) {
                         case 'null':
                             $compilationContext->backend->assignNull($variable, $compilationContext);
                             break;
                         case 'array':
                         case 'empty-array':
                             $compilationContext->backend->initArray($variable, $compilationContext, null);
                             break;
                         default:
                             throw new CompilerException('Invalid default type: ' . $defaultValue['type'] . ' for data type: ' . $variable->getType(), $variable->getOriginal());
                     }
                 }
             }
         }
     }
     $compilationContext->codePrinter = $oldCodePrinter;
     return $codePrinter->getOutput();
 }
Пример #7
0
 public function generateFunctionInformation()
 {
     $headerPrinter = new CodePrinter();
     $entryPrinter = new CodePrinter();
     /**
      * Create argument info
      */
     foreach ($this->functionDefinitions as $func) {
         $funcName = $func->getInternalName();
         $argInfoName = 'arginfo_' . strtolower($funcName);
         $headerPrinter->output('PHP_FUNCTION(' . $funcName . ');');
         $parameters = $func->getParameters();
         if (count($parameters)) {
             $headerPrinter->output('ZEND_BEGIN_ARG_INFO_EX(' . $argInfoName . ', 0, 0, ' . $func->getNumberOfRequiredParameters() . ')');
             foreach ($parameters->getParameters() as $parameter) {
                 switch ($parameter['data-type']) {
                     case 'array':
                         $headerPrinter->output("\t" . 'ZEND_ARG_ARRAY_INFO(0, ' . $parameter['name'] . ', ' . (isset($parameter['default']) ? 1 : 0) . ')');
                         break;
                     case 'variable':
                         if (isset($parameter['cast'])) {
                             switch ($parameter['cast']['type']) {
                                 case 'variable':
                                     $value = $parameter['cast']['value'];
                                     $headerPrinter->output("\t" . 'ZEND_ARG_OBJ_INFO(0, ' . $parameter['name'] . ', ' . Utils::escapeClassName($compilationContext->getFullName($value)) . ', ' . (isset($parameter['default']) ? 1 : 0) . ')');
                                     break;
                                 default:
                                     throw new Exception('Unexpected exception');
                             }
                         } else {
                             $headerPrinter->output("\t" . 'ZEND_ARG_INFO(0, ' . $parameter['name'] . ')');
                         }
                         break;
                     default:
                         $headerPrinter->output("\t" . 'ZEND_ARG_INFO(0, ' . $parameter['name'] . ')');
                         break;
                 }
             }
             $headerPrinter->output('ZEND_END_ARG_INFO()');
             $headerPrinter->outputBlankLine();
         }
         /** Generate FE's */
         $paramData = count($parameters) ? $argInfoName : 'NULL';
         if ($func->isGlobal()) {
             $entryPrinter->output('ZEND_NAMED_FE(' . $func->getName() . ', ZEND_FN(' . $funcName . '), ' . $paramData . ')');
         } else {
             $entryPrinter->output('ZEND_NS_NAMED_FE("' . str_replace('\\', '\\\\', $func->getNamespace()) . '", ' . $func->getName() . ', ZEND_FN(' . $funcName . '), ' . $paramData . ')');
         }
     }
     $entryPrinter->output('ZEND_FE_END');
     return array($headerPrinter->getOutput(), $entryPrinter->getOutput());
 }
Пример #8
0
 public function genFcallCode()
 {
     $codePrinter = new CodePrinter();
     $codePrinter->output('#ifndef ZEPHIR_KERNEL_FCALL_INTERNAL_H');
     $codePrinter->output('#define ZEPHIR_KERNEL_FCALL_INTERNAL_H');
     $codePrinter->increaseLevel();
     ksort($this->requiredMacros);
     foreach ($this->requiredMacros as $name => $info) {
         list($scope, $mode, $paramCount) = $info;
         $paramsStr = '';
         $retParam = '';
         $retValueUsed = '0';
         $params = array();
         $zvals = array();
         $initStatements = array();
         $postStatements = array();
         for ($i = 0; $i < $paramCount; ++$i) {
             $params[] = 'p' . $i;
         }
         if ($paramCount) {
             $paramsStr = ', ' . implode(', ', $params);
         }
         if ($mode == 'CALL_INTERNAL_METHOD_P') {
             $retValueUsed = '1';
             $retParam = 'return_value_ptr';
             $initStatements[] = 'ZEPHIR_INIT_NVAR(*(return_value_ptr)); \\';
         }
         $objParam = $scope ? 'scope_ce, ' : 'object, ';
         $macroName = $name . '(' . ($retParam ? $retParam . ', ' : '') . $objParam . 'method' . $paramsStr . ')';
         $codePrinter->output('#define ' . $macroName . ' \\');
         if (!$retParam) {
             $retParam = 'return_value';
         }
         $codePrinter->increaseLevel();
         $codePrinter->output('do { \\');
         $codePrinter->increaseLevel();
         if ($mode == 'CALL_INTERNAL_METHOD_NORETURN_P') {
             $codePrinter->output('zval *rv = NULL; \\');
             $codePrinter->output('zval **rvp = &rv; \\');
             $codePrinter->output('ALLOC_INIT_ZVAL(rv); \\');
             $retParam = 'rvp';
         }
         $codePrinter->output('ZEPHIR_BACKUP_SCOPE() \\');
         $codePrinter->output('ZEPHIR_BACKUP_THIS_PTR() \\');
         if (!$scope) {
             $codePrinter->output('ZEPHIR_SET_THIS(object); \\');
             $codePrinter->output('ZEPHIR_SET_SCOPE((Z_TYPE_P(object) == IS_OBJECT ? Z_OBJCE_P(object) : NULL), (Z_TYPE_P(object) == IS_OBJECT ? Z_OBJCE_P(object) : NULL)); \\');
         } else {
             $codePrinter->output('ZEPHIR_SET_THIS(NULL); \\');
             $codePrinter->output('ZEPHIR_SET_SCOPE(scope_ce, scope_ce); \\');
         }
         /* Create new zval's for parameters */
         for ($i = 0; $i < $paramCount; ++$i) {
             //$zv = '_' . $params[$i];
             //$zvals[] = $zv;
             //$initStatements[] = 'ALLOC_ZVAL(' . $zv . '); \\';
             //$initStatements[] = 'INIT_PZVAL_COPY(' . $zv . ', ' . $params[$i] . '); \\';
             //$postStatements[] = 'zval_ptr_dtor(&' . $zv . '); \\';
             $zv = $params[$i];
             $zvals[] = $zv;
             $initStatements[] = 'Z_ADDREF_P(' . $zv . '); \\';
             $postStatements[] = 'Z_DELREF_P(' . $zv . '); \\';
         }
         if ($i) {
             //$codePrinter->output('zval *' . implode(', *', $zvals) . '; \\');
         }
         foreach ($initStatements as $statement) {
             $codePrinter->output($statement);
         }
         $zvalStr = $i ? ', ' . implode(', ', $zvals) : '';
         $retExpr = '';
         if ($retParam) {
             if ($retParam == 'return_value') {
                 $retExpr = ', return_value, return_value_ptr';
             } else {
                 $retExpr = ', *' . $retParam . ', ' . $retParam;
             }
         }
         $codePrinter->output('method(0' . $retExpr . ', ' . ($scope ? 'NULL, ' : $objParam) . $retValueUsed . $zvalStr . ' TSRMLS_CC); \\');
         if ($mode == 'CALL_INTERNAL_METHOD_NORETURN_P') {
             $postStatements[] = 'zval_ptr_dtor(rvp); \\';
         }
         foreach ($postStatements as $statement) {
             $codePrinter->output($statement);
         }
         $codePrinter->output('ZEPHIR_LAST_CALL_STATUS = EG(exception) ? FAILURE : SUCCESS; \\');
         $codePrinter->output('ZEPHIR_RESTORE_THIS_PTR(); \\');
         $codePrinter->output('ZEPHIR_RESTORE_SCOPE(); \\');
         $codePrinter->decreaseLevel();
         $codePrinter->output('} while (0)');
         $codePrinter->decreaseLevel();
         $codePrinter->output('');
     }
     $codePrinter->decreaseLevel();
     $codePrinter->output("#endif");
     Utils::checkAndWriteIfNeeded($codePrinter->getOutput(), 'ext/kernel/fcall_internal.h');
 }
Пример #9
0
 /**
  * Compiles the method
  *
  * @param CompilationContext $compilationContext
  * @return null
  * @throws CompilerException
  */
 public function compile(CompilationContext $compilationContext)
 {
     /**
      * Set the method currently being compiled
      */
     $compilationContext->currentMethod = $this;
     if (is_object($this->_statements)) {
         /**
          * This pass checks for zval variables than can be potentially
          * used without allocating memory and track it
          * these variables are stored in the stack
          */
         if ($compilationContext->config->get('local-context-pass', 'optimizations')) {
             $localContext = new LocalContextPass();
             $localContext->pass($this->_statements);
         } else {
             $localContext = null;
         }
         /**
          * This pass tries to infer types for dynamic variables
          * replacing them by low level variables
          */
         if ($compilationContext->config->get('static-type-inference', 'optimizations')) {
             $typeInference = new StaticTypeInference();
             $typeInference->pass($this->_statements);
             if ($compilationContext->config->get('static-type-inference-second-pass', 'optimizations')) {
                 $typeInference->reduce();
                 $typeInference->pass($this->_statements);
             }
         } else {
             $typeInference = null;
         }
         /**
          * This pass counts how many times a specific
          */
         if ($compilationContext->config->get('call-gatherer-pass', 'optimizations')) {
             $callGathererPass = new CallGathererPass($compilationContext);
             $callGathererPass->pass($this->_statements);
         } else {
             $callGathererPass = null;
         }
     } else {
         $localContext = null;
         $typeInference = null;
         $callGathererPass = null;
     }
     /**
      * Every method has its own symbol table
      */
     $symbolTable = new SymbolTable($compilationContext);
     if ($localContext) {
         $symbolTable->setLocalContext($localContext);
     }
     /**
      * Parameters has an additional extra mutation
      */
     $parameters = $this->_parameters;
     if ($localContext) {
         if (is_object($parameters)) {
             foreach ($parameters->getParameters() as $parameter) {
                 $localContext->increaseMutations($parameter['name']);
             }
         }
     }
     /**
      * Initialization of parameters happens in a fictitious external branch
      */
     $branch = new Branch();
     $branch->setType(Branch::TYPE_EXTERNAL);
     /**
      * BranchManager helps to create graphs of conditional/loop/root/jump branches
      */
     $branchManager = new BranchManager();
     $branchManager->addBranch($branch);
     /**
      * Cache Manager manages both function and method call caches
      */
     $cacheManager = new CacheManager();
     $cacheManager->setGatherer($callGathererPass);
     $compilationContext->branchManager = $branchManager;
     $compilationContext->cacheManager = $cacheManager;
     $compilationContext->typeInference = $typeInference;
     $compilationContext->symbolTable = $symbolTable;
     $oldCodePrinter = $compilationContext->codePrinter;
     /**
      * Change the code printer to a single method instance
      */
     $codePrinter = new CodePrinter();
     $compilationContext->codePrinter = $codePrinter;
     /**
      * Set an empty function cache
      */
     $compilationContext->functionCache = null;
     /**
      * Reset try/catch and loop counter
      */
     $compilationContext->insideCycle = 0;
     $compilationContext->insideTryCatch = 0;
     if (is_object($parameters)) {
         /**
          * Round 1. Create variables in parameters in the symbol table
          */
         $classCastChecks = array();
         foreach ($parameters->getParameters() as $parameter) {
             /**
              * Change dynamic variables to low level types
              */
             if ($typeInference) {
                 if (isset($parameter['data-type'])) {
                     if ($parameter['data-type'] == 'variable') {
                         $type = $typeInference->getInferedType($parameter['name']);
                         if (is_string($type)) {
                             /* promote polymorphic parameters to low level types */
                         }
                     }
                 } else {
                     $type = $typeInference->getInferedType($parameter['name']);
                     if (is_string($type)) {
                         /* promote polymorphic parameters to low level types */
                     }
                 }
             }
             $symbolParam = null;
             if (isset($parameter['data-type'])) {
                 switch ($parameter['data-type']) {
                     case 'object':
                     case 'callable':
                     case 'resource':
                     case 'variable':
                         $symbol = $symbolTable->addVariable($parameter['data-type'], $parameter['name'], $compilationContext);
                         break;
                     default:
                         $symbol = $symbolTable->addVariable($parameter['data-type'], $parameter['name'], $compilationContext);
                         $symbolParam = $symbolTable->addVariable('variable', $parameter['name'] . '_param', $compilationContext);
                         if ($parameter['data-type'] == 'string' || $parameter['data-type'] == 'array') {
                             $symbol->setMustInitNull(true);
                         }
                         break;
                 }
             } else {
                 $symbol = $symbolTable->addVariable('variable', $parameter['name'], $compilationContext);
             }
             /**
              * Some parameters can be read-only
              */
             if (isset($parameter['const']) && $parameter['const']) {
                 $symbol->setReadOnly(true);
                 if (is_object($symbolParam)) {
                     $symbolParam->setReadOnly(true);
                 }
             }
             if (is_object($symbolParam)) {
                 /**
                  * Parameters are marked as 'external'
                  */
                 $symbolParam->setIsExternal(true);
                 /**
                  * Assuming they're initialized
                  */
                 $symbolParam->setIsInitialized(true, $compilationContext, $parameter);
                 /**
                  * Initialize auxiliar parameter zvals to null
                  */
                 $symbolParam->setMustInitNull(true);
                 /**
                  * Increase uses
                  */
                 $symbolParam->increaseUses();
             } else {
                 if (isset($parameter['default'])) {
                     if (isset($parameter['data-type'])) {
                         if ($parameter['data-type'] == 'variable') {
                             $symbol->setMustInitNull(true);
                         }
                     } else {
                         $symbol->setMustInitNull(true);
                     }
                 }
             }
             /**
              * Original node where the variable was declared
              */
             $symbol->setOriginal($parameter);
             /**
              * Parameters are marked as 'external'
              */
             $symbol->setIsExternal(true);
             /**
              * Assuming they're initialized
              */
             $symbol->setIsInitialized(true, $compilationContext, $parameter);
             /**
              * Variables with class/type must be objects across the execution
              */
             if (isset($parameter['cast'])) {
                 $symbol->setDynamicTypes('object');
                 $symbol->setClassTypes($compilationContext->getFullName($parameter['cast']['value']));
                 $classCastChecks[] = array($symbol, $parameter);
             } else {
                 if (isset($parameter['data-type'])) {
                     if ($parameter['data-type'] == 'variable') {
                         $symbol->setDynamicTypes('undefined');
                     }
                 } else {
                     $symbol->setDynamicTypes('undefined');
                 }
             }
         }
         $compilationContext->codePrinter->increaseLevel();
         /**
          * Checks that a class-hinted variable meets its declaration
          */
         foreach ($classCastChecks as $classCastCheck) {
             foreach ($classCastCheck[0]->getClassTypes() as $className) {
                 /**
                  * If the parameter is nullable check it must pass the 'instanceof' validation
                  */
                 if (!isset($classCastCheck[1]['default'])) {
                     $evalExpr = new UnaryOperatorBuilder('not', new BinaryOperatorBuilder('instanceof', new VariableBuilder($classCastCheck[0]->getName()), new VariableBuilder('\\' . $className)));
                 } else {
                     $evalExpr = new BinaryOperatorBuilder('and', new BinaryOperatorBuilder('not-equals', new TypeOfOperatorBuilder(new VariableBuilder($classCastCheck[0]->getName())), new LiteralBuilder("string", "null")), new UnaryOperatorBuilder('not', new BinaryOperatorBuilder('instanceof', new VariableBuilder($classCastCheck[0]->getName()), new VariableBuilder('\\' . $className))));
                 }
                 $ifCheck = new IfStatementBuilder($evalExpr, new StatementsBlockBuilder(array(new ThrowStatementBuilder(new NewInstanceOperatorBuilder('\\InvalidArgumentException', array(new ParameterBuilder(new LiteralBuilder("string", "Parameter '" . $classCastCheck[0]->getName() . "' must be an instance of '" . Utils::escapeClassName($className) . "'"))))))));
                 $ifStatement = new IfStatement($ifCheck->get());
                 $ifStatement->compile($compilationContext);
             }
         }
         $compilationContext->codePrinter->decreaseLevel();
     }
     /**
      * Compile the block of statements if any
      */
     if (is_object($this->_statements)) {
         if ($this->hasModifier('static')) {
             $compilationContext->staticContext = true;
         } else {
             $compilationContext->staticContext = false;
         }
         /**
          * Compile the statements block as a 'root' branch
          */
         $this->_statements->compile($compilationContext, false, Branch::TYPE_ROOT);
     }
     /**
      * Initialize default values in dynamic variables
      */
     $initVarCode = "";
     foreach ($symbolTable->getVariables() as $variable) {
         /**
          * Initialize 'dynamic' variables with default values
          */
         if ($variable->getType() == 'variable') {
             if ($variable->getNumberUses() > 0) {
                 if ($variable->getName() != 'this_ptr' && $variable->getName() != 'return_value' && $variable->getName() != 'return_value_ptr') {
                     $defaultValue = $variable->getDefaultInitValue();
                     if (is_array($defaultValue)) {
                         $symbolTable->mustGrownStack(true);
                         switch ($defaultValue['type']) {
                             case 'int':
                             case 'uint':
                             case 'long':
                             case 'char':
                             case 'uchar':
                                 $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                                 $initVarCode .= "\t" . 'ZVAL_LONG(' . $variable->getName() . ', ' . $defaultValue['value'] . ');' . PHP_EOL;
                                 break;
                             case 'null':
                                 $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                                 $initVarCode .= "\t" . 'ZVAL_NULL(' . $variable->getName() . ');' . PHP_EOL;
                                 break;
                             case 'double':
                                 $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                                 $initVarCode .= "\t" . 'ZVAL_DOUBLE(' . $variable->getName() . ', ' . $defaultValue['value'] . ');' . PHP_EOL;
                                 break;
                             case 'string':
                                 $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                                 $initVarCode .= "\t" . 'ZVAL_STRING(' . $variable->getName() . ', "' . Utils::addSlashes($defaultValue['value'], true) . '", 1);' . PHP_EOL;
                                 break;
                             case 'array':
                             case 'empty-array':
                                 $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                                 $initVarCode .= "\t" . 'array_init(' . $variable->getName() . ');' . PHP_EOL;
                                 break;
                             default:
                                 throw new CompilerException('Invalid default type: ' . $defaultValue['type'] . ' for data type: ' . $variable->getType(), $variable->getOriginal());
                         }
                     }
                 }
             }
             continue;
         }
         /**
          * Initialize 'string' variables with default values
          */
         if ($variable->getType() == 'string') {
             if ($variable->getNumberUses() > 0) {
                 $defaultValue = $variable->getDefaultInitValue();
                 if (is_array($defaultValue)) {
                     $symbolTable->mustGrownStack(true);
                     switch ($defaultValue['type']) {
                         case 'string':
                             $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                             $initVarCode .= "\t" . 'ZVAL_STRING(' . $variable->getName() . ', "' . Utils::addSlashes($defaultValue['value'], true) . '", 1);' . PHP_EOL;
                             break;
                         case 'null':
                             $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                             $initVarCode .= "\t" . 'ZVAL_EMPTY_STRING(' . $variable->getName() . ');' . PHP_EOL;
                             break;
                         default:
                             throw new CompilerException('Invalid default type: ' . $defaultValue['type'] . ' for data type: ' . $variable->getType(), $variable->getOriginal());
                     }
                 }
             }
             continue;
         }
         /**
          * Initialize 'array' variables with default values
          */
         if ($variable->getType() == 'array') {
             if ($variable->getNumberUses() > 0) {
                 $defaultValue = $variable->getDefaultInitValue();
                 if (is_array($defaultValue)) {
                     $symbolTable->mustGrownStack(true);
                     switch ($defaultValue['type']) {
                         case 'null':
                             $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                             $initVarCode .= "\t" . 'ZVAL_NULL(' . $variable->getName() . ');' . PHP_EOL;
                             break;
                         case 'array':
                         case 'empty-array':
                             $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL;
                             $initVarCode .= "\t" . 'array_init(' . $variable->getName() . ');' . PHP_EOL;
                             break;
                         default:
                             throw new CompilerException('Invalid default type: ' . $defaultValue['type'] . ' for data type: ' . $variable->getType(), $variable->getOriginal());
                     }
                 }
             }
         }
     }
     /**
      * Fetch parameters from vm-top
      */
     $initCode = "";
     $code = "";
     if (is_object($parameters)) {
         /**
          * Round 2. Fetch the parameters in the method
          */
         $params = array();
         $requiredParams = array();
         $optionalParams = array();
         $numberRequiredParams = 0;
         $numberOptionalParams = 0;
         foreach ($parameters->getParameters() as $parameter) {
             if (isset($parameter['data-type'])) {
                 $dataType = $parameter['data-type'];
             } else {
                 $dataType = 'variable';
             }
             switch ($dataType) {
                 case 'object':
                 case 'callable':
                 case 'resource':
                 case 'variable':
                     $params[] = '&' . $parameter['name'];
                     break;
                 default:
                     $params[] = '&' . $parameter['name'] . '_param';
                     break;
             }
             if (isset($parameter['default'])) {
                 $optionalParams[] = $parameter;
                 $numberOptionalParams++;
             } else {
                 $requiredParams[] = $parameter;
                 $numberRequiredParams++;
             }
         }
         /**
          * Pass the write detector to the method statement block to check if the parameter
          * variable is modified so as do the proper separation
          */
         $parametersToSeparate = array();
         if (is_object($this->_statements)) {
             /**
              * If local context is not available
              */
             if (!$localContext) {
                 $writeDetector = new WriteDetector();
             }
             foreach ($parameters->getParameters() as $parameter) {
                 if (isset($parameter['data-type'])) {
                     $dataType = $parameter['data-type'];
                 } else {
                     $dataType = 'variable';
                 }
                 switch ($dataType) {
                     case 'variable':
                     case 'string':
                     case 'array':
                     case 'resource':
                     case 'object':
                     case 'callable':
                         $name = $parameter['name'];
                         if (!$localContext) {
                             if ($writeDetector->detect($name, $this->_statements->getStatements())) {
                                 $parametersToSeparate[$name] = true;
                             }
                         } else {
                             if ($localContext->getNumberOfMutations($name) > 1) {
                                 $parametersToSeparate[$name] = true;
                             }
                         }
                         break;
                 }
             }
         }
         /**
          * Initialize required parameters
          */
         foreach ($requiredParams as $parameter) {
             if (isset($parameter['mandatory'])) {
                 $mandatory = $parameter['mandatory'];
             } else {
                 $mandatory = 0;
             }
             if (isset($parameter['data-type'])) {
                 $dataType = $parameter['data-type'];
             } else {
                 $dataType = 'variable';
             }
             if ($dataType != 'variable') {
                 /**
                  * Assign value from zval to low level type
                  */
                 if ($mandatory) {
                     $initCode .= $this->checkStrictType($parameter, $compilationContext);
                 } else {
                     $initCode .= $this->assignZvalValue($parameter, $compilationContext);
                 }
             }
             switch ($dataType) {
                 case 'variable':
                 case 'string':
                 case 'array':
                 case 'resource':
                 case 'object':
                 case 'callable':
                     if (isset($parametersToSeparate[$parameter['name']])) {
                         $symbolTable->mustGrownStack(true);
                         $initCode .= "\t" . "ZEPHIR_SEPARATE_PARAM(" . $parameter['name'] . ");" . PHP_EOL;
                     }
                     break;
             }
         }
         /**
          * Initialize optional parameters
          */
         foreach ($optionalParams as $parameter) {
             if (isset($parameter['mandatory'])) {
                 $mandatory = $parameter['mandatory'];
             } else {
                 $mandatory = 0;
             }
             if (isset($parameter['data-type'])) {
                 $dataType = $parameter['data-type'];
             } else {
                 $dataType = 'variable';
             }
             switch ($dataType) {
                 case 'object':
                 case 'callable':
                 case 'resource':
                 case 'variable':
                     $name = $parameter['name'];
                     break;
                 default:
                     $name = $parameter['name'] . '_param';
                     break;
             }
             /**
              * Assign the default value according to the variable's type
              */
             $initCode .= "\t" . 'if (!' . $name . ') {' . PHP_EOL;
             $initCode .= $this->assignDefaultValue($parameter, $compilationContext);
             if (isset($parametersToSeparate[$name]) || $dataType != 'variable') {
                 $initCode .= "\t" . '} else {' . PHP_EOL;
                 if (isset($parametersToSeparate[$name])) {
                     $initCode .= "\t\t" . "ZEPHIR_SEPARATE_PARAM(" . $name . ");" . PHP_EOL;
                 } else {
                     if ($mandatory) {
                         $initCode .= $this->checkStrictType($parameter, $compilationContext, $mandatory);
                     } else {
                         $initCode .= "\t" . $this->assignZvalValue($parameter, $compilationContext);
                     }
                 }
             }
             $initCode .= "\t" . '}' . PHP_EOL;
         }
         /**
          * Fetch the parameters to zval pointers
          */
         $codePrinter->preOutputBlankLine();
         $compilationContext->headersManager->add('kernel/memory');
         if ($symbolTable->getMustGrownStack()) {
             $code .= "\t" . 'zephir_fetch_params(1, ' . $numberRequiredParams . ', ' . $numberOptionalParams . ', ' . join(', ', $params) . ');' . PHP_EOL;
         } else {
             $code .= "\t" . 'zephir_fetch_params(0, ' . $numberRequiredParams . ', ' . $numberOptionalParams . ', ' . join(', ', $params) . ');' . PHP_EOL;
         }
         $code .= PHP_EOL;
     }
     $code .= $initCode . $initVarCode;
     $codePrinter->preOutput($code);
     /**
      * Grow the stack if needed
      */
     if ($symbolTable->getMustGrownStack()) {
         $compilationContext->headersManager->add('kernel/memory');
         $codePrinter->preOutput("\t" . 'ZEPHIR_MM_GROW();');
     }
     /**
      * Check if there are unused variables
      */
     $usedVariables = array();
     $completeName = $compilationContext->classDefinition->getCompleteName();
     foreach ($symbolTable->getVariables() as $variable) {
         if ($variable->getNumberUses() <= 0) {
             if ($variable->isExternal() == false) {
                 $compilationContext->logger->warning('Variable "' . $variable->getName() . '" declared but not used in ' . $completeName . '::' . $this->getName(), "unused-variable", $variable->getOriginal());
                 continue;
             }
             $compilationContext->logger->warning('Variable "' . $variable->getName() . '" declared but not used in ' . $completeName . '::' . $this->getName(), "unused-variable-external", $variable->getOriginal());
         }
         if ($variable->getName() != 'this_ptr' && $variable->getName() != 'return_value' && $variable->getName() != 'return_value_ptr') {
             $type = $variable->getType();
             if (!isset($usedVariables[$type])) {
                 $usedVariables[$type] = array();
             }
             $usedVariables[$type][] = $variable;
         }
     }
     if (count($usedVariables)) {
         $codePrinter->preOutputBlankLine();
     }
     /**
      * Generate the variable definition for variables used
      */
     foreach ($usedVariables as $type => $variables) {
         $pointer = null;
         switch ($type) {
             case 'int':
                 $code = 'int ';
                 break;
             case 'uint':
                 $code = 'unsigned int ';
                 break;
             case 'char':
                 $code = 'char ';
                 break;
             case 'uchar':
                 $code = 'unsigned char ';
                 break;
             case 'long':
                 $code = 'long ';
                 break;
             case 'ulong':
                 $code = 'unsigned long ';
                 break;
             case 'bool':
                 $code = 'zend_bool ';
                 break;
             case 'double':
                 $code = 'double ';
                 break;
             case 'string':
             case 'variable':
             case 'array':
             case 'null':
                 $pointer = '*';
                 $code = 'zval ';
                 break;
             case 'HashTable':
                 $pointer = '*';
                 $code = 'HashTable ';
                 break;
             case 'HashPosition':
                 $code = 'HashPosition ';
                 break;
             case 'zend_class_entry':
                 $pointer = '*';
                 $code = 'zend_class_entry ';
                 break;
             case 'zend_function':
                 $pointer = '*';
                 $code = 'zend_function ';
                 break;
             case 'zend_object_iterator':
                 $pointer = '*';
                 $code = 'zend_object_iterator ';
                 break;
             case 'zend_property_info':
                 $pointer = '*';
                 $code = 'zend_property_info ';
                 break;
             case 'zephir_fcall_cache_entry':
                 $pointer = '*';
                 $code = 'zephir_fcall_cache_entry ';
                 break;
             case 'static_zephir_fcall_cache_entry':
                 $pointer = '*';
                 $code = 'zephir_nts_static zephir_fcall_cache_entry ';
                 break;
             default:
                 throw new CompilerException("Unsupported type in declare: " . $type);
         }
         $groupVariables = array();
         $defaultValues = array();
         /**
          * @var $variables Variable[]
          */
         foreach ($variables as $variable) {
             if (($type == 'variable' || $type == 'string' || $type == 'array' || $type == 'resource' || $type == 'callable' || $type == 'object') && $variable->mustInitNull()) {
                 if ($variable->isLocalOnly()) {
                     $groupVariables[] = $variable->getName() . ' = zval_used_for_init';
                 } else {
                     if ($variable->isDoublePointer()) {
                         $groupVariables[] = $pointer . $pointer . $variable->getName() . ' = NULL';
                     } else {
                         $groupVariables[] = $pointer . $variable->getName() . ' = NULL';
                     }
                 }
             } else {
                 if ($variable->isLocalOnly()) {
                     $groupVariables[] = $variable->getName();
                 } else {
                     if ($variable->isDoublePointer()) {
                         if ($variable->mustInitNull()) {
                             $groupVariables[] = $pointer . $pointer . $variable->getName() . ' = NULL';
                         } else {
                             $groupVariables[] = $pointer . $pointer . $variable->getName();
                         }
                     } else {
                         $defaultValue = $variable->getDefaultInitValue();
                         if ($defaultValue !== null) {
                             switch ($type) {
                                 case 'variable':
                                 case 'string':
                                 case 'array':
                                 case 'resource':
                                 case 'callable':
                                 case 'object':
                                     $groupVariables[] = $pointer . $variable->getName();
                                     break;
                                 default:
                                     $groupVariables[] = $pointer . $variable->getName() . ' = ' . $defaultValue;
                                     break;
                             }
                         } else {
                             if ($variable->mustInitNull() && $pointer) {
                                 $groupVariables[] = $pointer . $variable->getName() . ' = NULL';
                             } else {
                                 $groupVariables[] = $pointer . $variable->getName();
                             }
                         }
                     }
                 }
             }
         }
         $codePrinter->preOutput("\t" . $code . join(', ', $groupVariables) . ';');
     }
     /**
      * Finalize the method compilation
      */
     if (is_object($this->_statements)) {
         /**
          * If the last statement is not a 'return' or 'throw' we need to
          * restore the memory stack if needed
          */
         $lastType = $this->_statements->getLastStatementType();
         if ($lastType != 'return' && $lastType != 'throw' && !$this->hasChildReturnStatementType($this->_statements->getLastStatement())) {
             if ($symbolTable->getMustGrownStack()) {
                 $compilationContext->headersManager->add('kernel/memory');
                 $codePrinter->output("\t" . 'ZEPHIR_MM_RESTORE();');
             }
             /**
              * If a method has return-type hints we need to ensure the last statement is a 'return' statement
              */
             if ($this->hasReturnTypes()) {
                 throw new CompilerException('Reached end of the method without returning a valid type specified in the return-type hints', $this->_expression['return-type']);
             }
         }
     }
     /**
      * Remove macros that restore the memory stack if it wasn't used
      */
     $code = $this->removeMemoryStackReferences($symbolTable, $codePrinter->getOutput());
     /**
      * Restore the compilation context
      */
     $oldCodePrinter->output($code);
     $compilationContext->codePrinter = $oldCodePrinter;
     $compilationContext->branchManager = null;
     $compilationContext->cacheManager = null;
     $compilationContext->typeInference = null;
     $codePrinter->clear();
     return null;
 }
Пример #10
0
 /**
  * Throws an exception escaping the data
  *
  * @param CodePrinter $printer
  * @param string $class
  * @param string $message
  * @param array $expression
  */
 private function throwStringException(CodePrinter $printer, $class, $message, $expression)
 {
     $message = Utils::addSlashes($message);
     $path = Compiler::getShortUserPath($expression['file']);
     $printer->output(sprintf('ZEPHIR_THROW_EXCEPTION_DEBUG_STR(%s, "%s", "%s", %s);', $class, $message, $path, $expression['line']));
     $printer->output('return;');
 }