Call functions. By default functions are called in the PHP userland if an optimizer was not found or there is not a user-handler for it
Inheritance: extends Call
Ejemplo n.º 1
0
 /**
  * @param CompilationContext $compilationContext
  * @param boolean $unreachable
  * @param int $branchType
  * @return Branch
  */
 public function compile(CompilationContext $compilationContext, $unreachable = false, $branchType = Branch::TYPE_UNKNOWN)
 {
     $compilationContext->codePrinter->increaseLevel();
     $compilationContext->currentBranch++;
     /**
      * Create a new branch
      */
     $currentBranch = new Branch();
     $currentBranch->setType($branchType);
     $currentBranch->setUnreachable($unreachable);
     /**
      * Activate branch in the branch manager
      */
     $compilationContext->branchManager->addBranch($currentBranch);
     $this->_unreachable = $unreachable;
     $statements = $this->_statements;
     foreach ($statements as $statement) {
         /**
          * Generate GDB hints
          */
         if ($this->_debug) {
             if (isset($statement['file'])) {
                 if ($statement['type'] != 'declare' && $statement['type'] != 'comment') {
                     $compilationContext->codePrinter->outputNoIndent('#line ' . $statement['line'] . ' "' . $statement['file'] . '"');
                 }
             }
         }
         /**
          * Show warnings if code is generated when the 'unreachable state' is 'on'
          */
         if ($this->_unreachable === true) {
             switch ($statement['type']) {
                 case 'echo':
                     $compilationContext->logger->warning('Unreachable code', "unreachable-code", $statement['expressions'][0]);
                     break;
                 case 'let':
                     $compilationContext->logger->warning('Unreachable code', "unreachable-code", $statement['assignments'][0]);
                     break;
                 case 'fetch':
                 case 'fcall':
                 case 'mcall':
                 case 'scall':
                 case 'if':
                 case 'while':
                 case 'do-while':
                 case 'switch':
                 case 'for':
                 case 'return':
                 case 'c-block':
                     if (isset($statement['expr'])) {
                         $compilationContext->logger->warning('Unreachable code', "unreachable-code", $statement['expr']);
                     } else {
                         $compilationContext->logger->warning('Unreachable code', "unreachable-code", $statement);
                     }
                     break;
                 default:
                     $compilationContext->logger->warning('Unreachable code', "unreachable-code", $statement);
             }
         }
         switch ($statement['type']) {
             case 'let':
                 $letStatement = new LetStatement($statement);
                 $letStatement->compile($compilationContext);
                 break;
             case 'echo':
                 $echoStatement = new EchoStatement($statement);
                 $echoStatement->compile($compilationContext);
                 break;
             case 'declare':
                 $declareStatement = new DeclareStatement($statement);
                 $declareStatement->compile($compilationContext);
                 break;
             case 'if':
                 $ifStatement = new IfStatement($statement);
                 $ifStatement->compile($compilationContext);
                 break;
             case 'while':
                 $whileStatement = new WhileStatement($statement);
                 $whileStatement->compile($compilationContext);
                 break;
             case 'do-while':
                 $doWhileStatement = new DoWhileStatement($statement);
                 $doWhileStatement->compile($compilationContext);
                 break;
             case 'switch':
                 $switchStatement = new SwitchStatement($statement);
                 $switchStatement->compile($compilationContext);
                 break;
             case 'for':
                 $forStatement = new ForStatement($statement);
                 $forStatement->compile($compilationContext);
                 break;
             case 'return':
                 $returnStatement = new ReturnStatement($statement);
                 $returnStatement->compile($compilationContext);
                 $this->_unreachable = true;
                 break;
             case 'require':
                 $requireStatement = new RequireStatement($statement);
                 $requireStatement->compile($compilationContext);
                 break;
             case 'loop':
                 $loopStatement = new LoopStatement($statement);
                 $loopStatement->compile($compilationContext);
                 break;
             case 'break':
                 $breakStatement = new BreakStatement($statement);
                 $breakStatement->compile($compilationContext);
                 $this->_unreachable = true;
                 break;
             case 'continue':
                 $continueStatement = new ContinueStatement($statement);
                 $continueStatement->compile($compilationContext);
                 $this->_unreachable = true;
                 break;
             case 'unset':
                 $unsetStatement = new UnsetStatement($statement);
                 $unsetStatement->compile($compilationContext);
                 break;
             case 'throw':
                 $throwStatement = new ThrowStatement($statement);
                 $throwStatement->compile($compilationContext);
                 $this->_unreachable = true;
                 break;
             case 'try-catch':
                 $throwStatement = new TryCatchStatement($statement);
                 $throwStatement->compile($compilationContext);
                 $this->_unreachable = false;
                 break;
             case 'fetch':
                 $expr = new Expression($statement['expr']);
                 $expr->setExpectReturn(false);
                 $compiledExpression = $expr->compile($compilationContext);
                 $compilationContext->codePrinter->output($compiledExpression->getCode() . ';');
                 break;
             case 'mcall':
                 $methodCall = new MethodCall();
                 $expr = new Expression($statement['expr']);
                 $expr->setExpectReturn(false);
                 $methodCall->compile($expr, $compilationContext);
                 break;
             case 'fcall':
                 $functionCall = new FunctionCall();
                 $expr = new Expression($statement['expr']);
                 $expr->setExpectReturn(false);
                 $compiledExpression = $functionCall->compile($expr, $compilationContext);
                 switch ($compiledExpression->getType()) {
                     case 'int':
                     case 'double':
                     case 'uint':
                     case 'long':
                     case 'ulong':
                     case 'char':
                     case 'uchar':
                     case 'bool':
                         $compilationContext->codePrinter->output($compiledExpression->getCode() . ';');
                         break;
                 }
                 break;
             case 'scall':
                 $methodCall = new StaticCall();
                 $expr = new Expression($statement['expr']);
                 $expr->setExpectReturn(false);
                 $methodCall->compile($expr, $compilationContext);
                 break;
             case 'cblock':
                 $compilationContext->codePrinter->output($statement['value']);
                 break;
             case 'empty':
                 break;
             default:
                 throw new Exception('Unsupported statement: ' . $statement['type']);
         }
         if ($statement['type'] != 'comment') {
             $this->_lastStatement = $statement;
         }
     }
     /**
      * Traverses temporal variables created in a specific branch
      * marking them as idle
      */
     $compilationContext->symbolTable->markTemporalVariablesIdle($compilationContext);
     $compilationContext->branchManager->removeBranch($currentBranch);
     $compilationContext->currentBranch--;
     $compilationContext->codePrinter->decreaseLevel();
     return $currentBranch;
 }
Ejemplo n.º 2
0
 /**
  * Resolves an expression
  *
  * @param CompilationContext $compilationContext
  * @return bool|CompiledExpression|mixed
  * @throws CompilerException
  */
 public function compile(CompilationContext $compilationContext)
 {
     $expression = $this->_expression;
     $type = $expression['type'];
     $compilableExpression = null;
     switch ($type) {
         case 'null':
             return new LiteralCompiledExpression('null', null, $expression);
         case 'int':
         case 'integer':
             return new LiteralCompiledExpression('int', $expression['value'], $expression);
         case 'long':
         case 'double':
         case 'bool':
             return new LiteralCompiledExpression($type, $expression['value'], $expression);
         case 'string':
             if (!$this->_stringOperation) {
                 if (ctype_digit($expression['value'])) {
                     return new CompiledExpression('int', $expression['value'], $expression);
                 }
             }
             return new LiteralCompiledExpression('string', str_replace(PHP_EOL, '\\n', $expression['value']), $expression);
         case 'istring':
             return new LiteralCompiledExpression('istring', str_replace(PHP_EOL, '\\n', $expression['value']), $expression);
         case 'char':
             if (!strlen($expression['value'])) {
                 throw new CompilerException("Invalid empty char literal", $expression);
             }
             if (strlen($expression['value']) > 2) {
                 if (strlen($expression['value']) > 10) {
                     throw new CompilerException("Invalid char literal: '" . substr($expression['value'], 0, 10) . "...'", $expression);
                 } else {
                     throw new CompilerException("Invalid char literal: '" . $expression['value'] . "'", $expression);
                 }
             }
             return new LiteralCompiledExpression('char', $expression['value'], $expression);
         case 'variable':
             $var = $compilationContext->symbolTable->getVariable($expression['value']);
             if ($var) {
                 if ($var->getRealName() == 'this') {
                     $var = 'this';
                 } else {
                     $var = $var->getName();
                 }
             } else {
                 $var = $expression['value'];
             }
             return new CompiledExpression('variable', $var, $expression);
         case 'constant':
             $compilableExpression = new Constants();
             break;
         case 'empty-array':
             return $this->emptyArray($expression, $compilationContext);
         case 'array-access':
             $compilableExpression = new NativeArrayAccess();
             $compilableExpression->setNoisy($this->isNoisy());
             break;
         case 'property-access':
             $compilableExpression = new PropertyAccess();
             $compilableExpression->setNoisy($this->isNoisy());
             break;
         case 'property-string-access':
         case 'property-dynamic-access':
             $compilableExpression = new PropertyDynamicAccess();
             $compilableExpression->setNoisy($this->isNoisy());
             break;
         case 'static-constant-access':
             $compilableExpression = new StaticConstantAccess();
             break;
         case 'static-property-access':
             $compilableExpression = new StaticPropertyAccess();
             break;
         case 'fcall':
             $functionCall = new FunctionCall();
             return $functionCall->compile($this, $compilationContext);
         case 'mcall':
             $methodCall = new MethodCall();
             return $methodCall->compile($this, $compilationContext);
         case 'scall':
             $staticCall = new StaticCall();
             return $staticCall->compile($this, $compilationContext);
         case 'isset':
             $compilableExpression = new IssetOperator();
             break;
         case 'fetch':
             $compilableExpression = new FetchOperator();
             break;
         case 'empty':
             $compilableExpression = new EmptyOperator();
             break;
         case 'array':
             $compilableExpression = new NativeArray();
             break;
         case 'new':
             $compilableExpression = new NewInstanceOperator();
             break;
         case 'new-type':
             $compilableExpression = new NewInstanceTypeOperator();
             break;
         case 'not':
             $compilableExpression = new NotOperator();
             break;
         case 'bitwise_not':
             $compilableExpression = new BitwiseNotOperator();
             break;
         case 'equals':
             $compilableExpression = new EqualsOperator();
             break;
         case 'not-equals':
             $compilableExpression = new NotEqualsOperator();
             break;
         case 'identical':
             $compilableExpression = new IdenticalOperator();
             break;
         case 'not-identical':
             $compilableExpression = new NotIdenticalOperator();
             break;
         case 'greater':
             $compilableExpression = new GreaterOperator();
             break;
         case 'less':
             $compilableExpression = new LessOperator();
             break;
         case 'less-equal':
             $compilableExpression = new LessEqualOperator();
             break;
         case 'greater-equal':
             $compilableExpression = new GreaterEqualOperator();
             break;
         case 'add':
             $compilableExpression = new AddOperator();
             break;
         case 'minus':
             $compilableExpression = new MinusOperator();
             break;
         case 'sub':
             $compilableExpression = new SubOperator();
             break;
         case 'mul':
             $compilableExpression = new MulOperator();
             break;
         case 'div':
             $compilableExpression = new DivOperator();
             break;
         case 'mod':
             $compilableExpression = new ModOperator();
             break;
         case 'and':
             $compilableExpression = new AndOperator();
             break;
         case 'or':
             $compilableExpression = new OrOperator();
             break;
         case 'bitwise_and':
             $compilableExpression = new BitwiseAndOperator();
             break;
         case 'bitwise_or':
             $compilableExpression = new BitwiseOrOperator();
             break;
         case 'bitwise_xor':
             $compilableExpression = new BitwiseXorOperator();
             break;
         case 'bitwise_shiftleft':
             $compilableExpression = new ShiftLeftOperator();
             break;
         case 'bitwise_shiftright':
             $compilableExpression = new ShiftRightOperator();
             break;
         case 'concat':
             $expr = new ConcatOperator();
             $expr->setExpectReturn($this->_expecting, $this->_expectingVariable);
             return $expr->compile($expression, $compilationContext);
         case 'irange':
             $compilableExpression = new RangeInclusiveOperator();
             break;
         case 'erange':
             $compilableExpression = new RangeExclusiveOperator();
             break;
         case 'list':
             if ($expression['left']['type'] == 'list') {
                 $compilationContext->logger->warning("Unnecessary extra parentheses", "extra-parentheses", $expression);
             }
             $numberPrints = $compilationContext->codePrinter->getNumberPrints();
             $expr = new Expression($expression['left']);
             $expr->setExpectReturn($this->_expecting, $this->_expectingVariable);
             $resolved = $expr->compile($compilationContext);
             if ($compilationContext->codePrinter->getNumberPrints() - $numberPrints <= 1) {
                 if (strpos($resolved->getCode(), ' ') !== false) {
                     return new CompiledExpression($resolved->getType(), '(' . $resolved->getCode() . ')', $expression);
                 }
             }
             return $resolved;
         case 'cast':
             $compilableExpression = new CastOperator();
             break;
         case 'type-hint':
             return $this->compileTypeHint($expression, $compilationContext);
         case 'instanceof':
             $compilableExpression = new InstanceOfOperator();
             break;
         case 'clone':
             $compilableExpression = new CloneOperator();
             break;
         case 'ternary':
             $compilableExpression = new TernaryOperator();
             break;
         case 'short-ternary':
             $expr = new ShortTernaryOperator();
             $expr->setReadOnly($this->isReadOnly());
             $expr->setExpectReturn($this->_expecting, $this->_expectingVariable);
             return $expr->compile($expression, $compilationContext);
         case 'likely':
             if (!$this->_evalMode) {
                 throw new CompilerException("'likely' operator can only be used in evaluation expressions", $expression);
             }
             $expr = new LikelyOperator();
             $expr->setReadOnly($this->isReadOnly());
             return $expr->compile($expression, $compilationContext);
         case 'unlikely':
             if (!$this->_evalMode) {
                 throw new CompilerException("'unlikely' operator can only be used in evaluation expressions", $expression);
             }
             $expr = new UnlikelyOperator();
             $expr->setReadOnly($this->isReadOnly());
             return $expr->compile($expression, $compilationContext);
         case 'typeof':
             $compilableExpression = new TypeOfOperator();
             break;
         case 'require':
             $compilableExpression = new RequireOperator();
             break;
         case 'closure':
             $compilableExpression = new Closure();
             break;
         case 'closure-arrow':
             $compilableExpression = new ClosureArrow();
             break;
         case 'reference':
             $compilableExpression = new Reference();
             break;
         default:
             throw new CompilerException("Unknown expression: " . $type, $expression);
     }
     if (!$compilableExpression) {
         throw new CompilerException("Unknown expression passed as compilableExpression", $expression);
     }
     $compilableExpression->setReadOnly($this->isReadOnly());
     $compilableExpression->setExpectReturn($this->_expecting, $this->_expectingVariable);
     return $compilableExpression->compile($expression, $compilationContext);
 }
Ejemplo n.º 3
0
 /**
  * Generates the C sources from Zephir without compiling them
  *
  * @param CommandInterface $command
  * @return bool
  * @throws Exception
  */
 public function generate(CommandInterface $command)
 {
     /**
      * Get global namespace
      */
     $namespace = $this->checkDirectory();
     /**
      * Check whether there are external dependencies
      */
     $externalDependencies = $this->config->get('external-dependencies');
     if (is_array($externalDependencies)) {
         foreach ($externalDependencies as $dependencyNs => $location) {
             if (!file_exists($location)) {
                 throw new CompilerException('Location of dependency "' . $dependencyNs . '" does not exist. Check the config.json for more information');
             }
             $this->addExternalDependency($dependencyNs, $location);
         }
     }
     /**
      * Check if there are module/request/global destructors
      */
     $destructors = $this->config->get('destructors');
     if (is_array($destructors)) {
         $invokeDestructors = $this->processCodeInjection($destructors);
         $includes = $invokeDestructors[0];
         $destructors = $invokeDestructors[1];
     }
     /**
      * Check if there are module/request/global initializers
      */
     $initializers = $this->config->get('initializers');
     if (is_array($initializers)) {
         $invokeInitializers = $this->processCodeInjection($initializers);
         $includes = $invokeInitializers[0];
         $initializers = $invokeInitializers[1];
     }
     /**
      * Round 1. pre-compile all files in memory
      */
     $this->recursivePreCompile(str_replace('\\', DIRECTORY_SEPARATOR, $namespace));
     if (!count($this->files)) {
         throw new Exception("Zephir files to compile couldn't be found. Did you add a first class to the extension?");
     }
     /**
      * Round 2. Check 'extends' and 'implements' dependencies
      */
     foreach ($this->files as $compileFile) {
         $compileFile->checkDependencies($this);
     }
     /**
      * Sort the files by dependency ranking
      */
     $files = array();
     $rankedFiles = array();
     $this->calculateDependencies($this->files);
     foreach ($this->files as $rankFile) {
         $rank = $rankFile->getClassDefinition()->getDependencyRank();
         $rankedFiles[$rank][] = $rankFile;
     }
     krsort($rankedFiles);
     foreach ($rankedFiles as $rank => $rankFiles) {
         $files = array_merge($files, $rankFiles);
     }
     $this->files = $files;
     /**
      * Convert C-constants into PHP constants
      */
     $constantsSources = $this->config->get('constants-sources');
     if (is_array($constantsSources)) {
         $this->loadConstantsSources($constantsSources);
     }
     /**
      * Set extension globals
      */
     $globals = $this->config->get('globals');
     if (is_array($globals)) {
         $this->setExtensionGlobals($globals);
     }
     /**
      * Load function optimizers
      */
     if (self::$loadedPrototypes === false) {
         FunctionCall::addOptimizerDir(ZEPHIRPATH . 'Library/Optimizers/FunctionCall');
         $optimizerDirs = $this->config->get('optimizer-dirs');
         if (is_array($optimizerDirs)) {
             foreach ($optimizerDirs as $directory) {
                 FunctionCall::addOptimizerDir(realpath($directory));
             }
         }
         if (is_dir(ZEPHIRPATH . 'prototypes') && is_readable(ZEPHIRPATH . 'prototypes')) {
             /**
              * Load additional extension prototypes
              * @var $file \DirectoryIterator
              */
             foreach (new \DirectoryIterator(ZEPHIRPATH . 'prototypes') as $file) {
                 if (!$file->isDir()) {
                     $extension = str_replace('.php', '', $file);
                     if (!extension_loaded($extension)) {
                         require $file->getRealPath();
                     }
                 }
             }
         }
         self::$loadedPrototypes = true;
     }
     /**
      * Round 3. Compile all files to C sources
      */
     $files = array();
     $hash = "";
     foreach ($this->files as $compileFile) {
         /**
          * Only compile classes in the local extension, ignore external classes
          */
         if (!$compileFile->isExternal()) {
             $compileFile->compile($this, $this->stringManager);
             $compiledFile = $compileFile->getCompiledFile();
             $methods = array();
             $classDefinition = $compileFile->getClassDefinition();
             foreach ($classDefinition->getMethods() as $method) {
                 $methods[] = '[' . $method->getName() . ':' . join('-', $method->getVisibility()) . ']';
                 if ($method->isInitializer() && $method->isStatic()) {
                     $this->internalInitializers[] = "\t" . $method->getName() . '(TSRMLS_C);';
                 }
             }
             $files[] = $compiledFile;
             $hash .= '|' . $compiledFile . ':' . $classDefinition->getClassEntry() . '[' . join('|', $methods) . ']';
         }
     }
     /**
      * Round 3.2. Compile anonymous classes
      */
     foreach ($this->anonymousFiles as $compileFile) {
         $compileFile->compile($this, $this->stringManager);
         $compiledFile = $compileFile->getCompiledFile();
         $methods = array();
         $classDefinition = $compileFile->getClassDefinition();
         foreach ($classDefinition->getMethods() as $method) {
             $methods[] = '[' . $method->getName() . ':' . join('-', $method->getVisibility()) . ']';
         }
         $files[] = $compiledFile;
         $hash .= '|' . $compiledFile . ':' . $classDefinition->getClassEntry() . '[' . join('|', $methods) . ']';
     }
     $hash = md5($hash);
     $this->compiledFiles = $files;
     /**
      * Round 3.3. Load extra C-sources
      */
     $extraSources = $this->config->get('extra-sources');
     if (is_array($extraSources)) {
         $this->extraFiles = $extraSources;
     } else {
         $this->extraFiles = array();
     }
     /**
      * Round 3.4. Load extra classes sources
      */
     $extraClasses = $this->config->get('extra-classes');
     if (is_array($extraClasses)) {
         foreach ($extraClasses as $value) {
             if (isset($value['source'])) {
                 $this->extraFiles[] = $value['source'];
             }
         }
     }
     /**
      * Round 4. Create config.m4 and config.w32 files / Create project.c and project.h files
      */
     $namespace = str_replace('\\', '_', $namespace);
     $extensionName = $this->config->get('extension-name');
     if (empty($extensionName) || !is_string($extensionName)) {
         $extensionName = $namespace;
     }
     $needConfigure = $this->createConfigFiles($extensionName);
     $needConfigure |= $this->createProjectFiles($extensionName);
     $needConfigure |= $this->checkIfPhpized();
     /**
      * When a new file is added or removed we need to run configure again
      */
     if (!$command instanceof CommandGenerate) {
         if (!$this->fileSystem->exists(self::VERSION . '/compiled-files-sum')) {
             $needConfigure = true;
             $this->fileSystem->write(self::VERSION . '/compiled-files-sum', $hash);
         } else {
             if ($this->fileSystem->read(self::VERSION . '/compiled-files-sum') != $hash) {
                 $needConfigure = true;
                 $this->fileSystem->write(self::VERSION . '/compiled-files-sum', $hash);
             }
         }
     }
     /**
      * Round 5. Generate concatenation functions
      */
     $this->stringManager->genConcatCode();
     $this->fcallManager->genFcallCode();
     if ($this->config->get('stubs-run-after-generate', 'stubs')) {
         $this->stubs($command, true);
     }
     return $needConfigure;
 }
Ejemplo n.º 4
0
 /**
  * Compiles a for statement that use a 'range' as expression
  *
  * @param array $exprRaw
  * @param \CompilationContext $compilationContext
  * @return boolean
  */
 public function compileRange($exprRaw, CompilationContext $compilationContext)
 {
     if (!count($exprRaw['parameters'])) {
         return false;
     }
     if (count($exprRaw['parameters']) > 3) {
         return false;
     }
     $functionCall = new FunctionCall();
     $parameters = $functionCall->getResolvedParamsAsExpr($exprRaw['parameters'], $compilationContext, $exprRaw);
     if (count($parameters) != 2 && count($parameters) != 3) {
         throw new CompilerException("Wrong number of parameters", $this->_statement['expr']);
     }
     if ($parameters[0]->getType() != 'variable') {
         if (!$parameters[0]->isIntCompatibleType()) {
             return false;
         }
     }
     if ($parameters[1]->getType() != 'variable') {
         if (!$parameters[1]->isIntCompatibleType()) {
             return false;
         }
     }
     $codePrinter = $compilationContext->codePrinter;
     /**
      * Initialize 'key' variable
      */
     if (isset($this->_statement['key'])) {
         /**
          * This variable is used to check if the loop is in its first iteration
          */
         $keyVariable = $compilationContext->symbolTable->getTempVariableForWrite('long', $compilationContext, $this->_statement);
         $keyVariable->increaseUses();
     }
     /**
      * This variable is used to check if the loop is in its first iteration
      */
     $flagVariable = $compilationContext->symbolTable->getTempVariableForWrite('bool', $compilationContext, $this->_statement);
     if ($parameters[0]->getType() != 'variable') {
         $tempVariable = $compilationContext->symbolTable->addTemp($parameters[0]->getType(), $compilationContext);
     } else {
         $rangeVariable = $compilationContext->symbolTable->getVariableForRead($parameters[0]->getCode(), $compilationContext, $this->_statement['expr']);
         $tempVariable = $compilationContext->symbolTable->addTemp($rangeVariable->getType(), $compilationContext);
     }
     /**
      * Create a copy of the current value in the end of the range to avoid modify the range
      * inside the cycle
      */
     if ($parameters[1]->getType() != 'variable') {
         $upperBoundVariable = $compilationContext->symbolTable->getTempVariable($parameters[1]->getType(), $compilationContext);
     } else {
         $rangeVariable = $compilationContext->symbolTable->getVariableForRead($parameters[1]->getCode(), $compilationContext, $this->_statement['expr']);
         $upperBoundVariable = $compilationContext->symbolTable->getTempVariable($rangeVariable->getType(), $compilationContext);
     }
     /**
      * Create an implicit 'let' operation to set the current value in the upper bound of the range
      */
     $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'variable' => $upperBoundVariable->getName(), 'operator' => 'assign', 'expr' => array('type' => $parameters[1]->getType(), 'value' => $parameters[1]->getCode(), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']))));
     $statement->compile($compilationContext);
     if ($this->_statement['reverse']) {
         /**
          * Create an implicit 'let' operation for the initialize expression, @TODO use a builder
          */
         $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'variable' => $tempVariable->getName(), 'operator' => 'assign', 'expr' => array('type' => 'variable', 'value' => $upperBoundVariable->getName(), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']))));
     } else {
         /**
          * Create an implicit 'let' operation for the initialize expression, @TODO use a builder
          */
         $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'variable' => $tempVariable->getName(), 'operator' => 'assign', 'expr' => array('type' => $parameters[0]->getType(), 'value' => $parameters[0]->getCode(), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']))));
     }
     $statement->compile($compilationContext);
     /**
      * Initialize 'key' variable
      */
     if (isset($this->_statement['key'])) {
         $codePrinter->output($keyVariable->getName() . ' = 0;');
     }
     $codePrinter->output($flagVariable->getName() . ' = 0;');
     if ($this->_statement['reverse']) {
         $conditionExpr = array('type' => 'greater-equal', 'left' => array('type' => 'variable', 'value' => $tempVariable->getName()), 'right' => array('type' => $parameters[0]->getType(), 'value' => $parameters[0]->getCode()));
     } else {
         $conditionExpr = array('type' => 'less-equal', 'left' => array('type' => 'variable', 'value' => $tempVariable->getName()), 'right' => array('type' => 'variable', 'value' => $upperBoundVariable->getName()));
     }
     $expr = new EvalExpression();
     $condition = $expr->optimize($conditionExpr, $compilationContext);
     $codePrinter->output('if (' . $condition . ') {');
     $codePrinter->increaseLevel();
     /**
      * Inside a cycle
      */
     $compilationContext->insideCycle++;
     $codePrinter->output('while (1) {');
     $codePrinter->increaseLevel();
     $codePrinter->output('if (' . $flagVariable->getName() . ') {');
     $codePrinter->increaseLevel();
     if (isset($this->_statement['key'])) {
         $codePrinter->output($keyVariable->getName() . '++;');
     }
     if ($this->_statement['reverse']) {
         if (!isset($parameters[2])) {
             $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'decr', 'variable' => $tempVariable->getName(), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']))));
         } else {
             $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'operator' => 'sub-assign', 'variable' => $tempVariable->getName(), 'expr' => array('type' => $parameters[2]->getType(), 'value' => $parameters[2]->getCode(), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']))));
         }
     } else {
         if (!isset($parameters[2])) {
             $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'incr', 'variable' => $tempVariable->getName(), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']))));
         } else {
             $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'operator' => 'add-assign', 'variable' => $tempVariable->getName(), 'expr' => array('type' => $parameters[2]->getType(), 'value' => $parameters[2]->getCode(), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']))));
         }
     }
     $statement->compile($compilationContext);
     /**
      * Multi-line conditions would need to be regenerated here
      */
     $condition = $expr->optimize($conditionExpr, $compilationContext);
     $codePrinter->output('if (!(' . $condition . ')) {');
     $codePrinter->output("\t" . "break;");
     $codePrinter->output('}');
     $codePrinter->decreaseLevel();
     $codePrinter->output('} else {');
     $codePrinter->output("\t" . $flagVariable->getName() . ' = 1;');
     $codePrinter->output('}');
     /**
      * Initialize 'key' variable
      */
     if (isset($this->_statement['key'])) {
         /**
          * Check for anonymous variables
          */
         if ($this->_statement['key'] != '_') {
             $keyVariableName = $this->_statement['key'];
         } else {
             $keyVariableName = $keyVariable->getName();
         }
         /**
          * Create an implicit 'let' operation, @TODO use a builder
          */
         $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'variable' => $keyVariableName, 'operator' => 'assign', 'expr' => array('type' => 'variable', 'value' => $keyVariable->getName(), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']))));
         $statement->compile($compilationContext);
     }
     /**
      * Initialize 'value' variable
      */
     if (isset($this->_statement['value'])) {
         /**
          * Check for anonymous variables
          */
         if ($this->_statement['value'] != '_') {
             $valueVariable = $this->_statement['value'];
         } else {
             $valueVariable = $tempVariable->getName();
         }
         /**
          * Create an implicit 'let' operation, @TODO use a builder
          */
         $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'variable' => $valueVariable, 'operator' => 'assign', 'expr' => array('type' => 'variable', 'value' => $tempVariable->getName(), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']))));
         $statement->compile($compilationContext);
     }
     $codePrinter->decreaseLevel();
     /**
      * Compile statements in the 'for' block
      */
     if (isset($this->_statement['statements'])) {
         $st = new StatementsBlock($this->_statement['statements']);
         $st->isLoop(true);
         if (isset($this->_statement['key'])) {
             $st->getMutateGatherer()->increaseMutations($this->_statement['key']);
         }
         $st->getMutateGatherer()->increaseMutations($this->_statement['value']);
         $st->compile($compilationContext);
     }
     /**
      * Restore the cycle counter
      */
     $compilationContext->insideCycle--;
     $codePrinter->output('}');
     $codePrinter->decreaseLevel();
     $codePrinter->output('}');
 }
Ejemplo n.º 5
0
 /**
  * @param CommandInterface $command
  *
  * @return bool
  * @throws Exception
  */
 public function generate(CommandInterface $command)
 {
     /**
      * Get global namespace
      */
     $namespace = $this->checkDirectory();
     /**
      * Round 1. pre-compile all files in memory
      */
     $this->recursivePreCompile(str_replace('\\', DIRECTORY_SEPARATOR, $namespace));
     if (!count($this->files)) {
         throw new Exception("Zephir files to compile weren't found");
     }
     /**
      * Round 2. Check 'extends' and 'implements' dependencies
      */
     foreach ($this->files as $compileFile) {
         $compileFile->checkDependencies($this);
     }
     /**
      * Convert C-constants into PHP constants
      */
     $constantsSources = $this->config->get('constants-sources');
     if (is_array($constantsSources)) {
         $this->loadConstantsSources($constantsSources);
     }
     /**
      * Set extension globals
      */
     $globals = $this->config->get('globals');
     if (is_array($globals)) {
         $this->setExtensionGlobals($globals);
     }
     /**
      * Load function optimizers
      */
     if (self::$loadedPrototypes == false) {
         FunctionCall::addOptimizerDir(ZEPHIRPATH . 'Library/Optimizers/FunctionCall');
         $optimizerDirs = $this->config->get('optimizer-dirs');
         if (is_array($optimizerDirs)) {
             foreach ($optimizerDirs as $directory) {
                 FunctionCall::addOptimizerDir(realpath($directory));
             }
         }
         if (is_dir(ZEPHIRPATH . 'prototypes') && is_readable(ZEPHIRPATH . 'prototypes')) {
             /**
              * Load additional extension prototypes
              * @var $file \DirectoryIterator
              */
             foreach (new \DirectoryIterator(ZEPHIRPATH . 'prototypes') as $file) {
                 if (!$file->isDir()) {
                     $extension = str_replace('.php', '', $file);
                     if (!extension_loaded($extension)) {
                         require $file->getRealPath();
                     }
                 }
             }
         }
         self::$loadedPrototypes = true;
     }
     /**
      * Round 3. Compile all files to C sources
      */
     $files = array();
     $hash = "";
     foreach ($this->files as $compileFile) {
         $compileFile->compile($this, $this->stringManager);
         $compiledFile = $compileFile->getCompiledFile();
         $methods = array();
         $classDefinition = $compileFile->getClassDefinition();
         foreach ($classDefinition->getMethods() as $method) {
             $methods[] = '[' . $method->getName() . ':' . join('-', $method->getVisibility()) . ']';
         }
         $files[] = $compiledFile;
         $hash .= '|' . $compiledFile . ':' . $classDefinition->getClassEntry() . '[' . join('|', $methods) . ']';
     }
     /**
      * Round 3.2. Compile anonymous classes
      */
     foreach ($this->anonymousFiles as $compileFile) {
         $compileFile->compile($this, $this->stringManager);
         $compiledFile = $compileFile->getCompiledFile();
     }
     $hash = md5($hash);
     $this->compiledFiles = $files;
     /**
      * Round 3.3. Load extra C-sources
      */
     $extraSources = $this->config->get('extra-sources');
     if (is_array($extraSources)) {
         $this->extraFiles = $extraSources;
     } else {
         $this->extraFiles = array();
     }
     /**
      * Round 4. Create config.m4 and config.w32 files / Create project.c and project.h files
      */
     $namespace = str_replace('\\', '_', $namespace);
     $needConfigure = $this->createConfigFiles($namespace);
     $needConfigure |= $this->createProjectFiles($namespace);
     $needConfigure |= $this->checkIfPhpized();
     /**
      * When a new file is added or removed we need to run configure again
      */
     if (!$command instanceof CommandGenerate) {
         if (!$this->fileSystem->exists(self::VERSION . '/compiled-files-sum')) {
             $needConfigure = true;
             $this->fileSystem->write(self::VERSION . '/compiled-files-sum', $hash);
         } else {
             if ($this->fileSystem->read(self::VERSION . '/compiled-files-sum') != $hash) {
                 $needConfigure = true;
                 $this->fileSystem->write(self::VERSION . '/compiled-files-sum', $hash);
             }
         }
     }
     /**
      * Round 5. Generate the concatenation
      */
     $this->stringManager->genConcatCode();
     if ($this->config->get('stubs-run-after-generate', 'stubs')) {
         $this->stubs($command, true);
     }
     return $needConfigure;
 }